zzz-template 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Evgeny Sinitsyn
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,444 @@
1
+ # zzz-template
2
+
3
+ > The fastest, simplest JavaScript template engine with zero dependencies
4
+
5
+ [![npm version](https://img.shields.io/npm/v/zzz-template.svg)](https://www.npmjs.com/package/zzz-template)
6
+ [![npm bundle size](https://img.shields.io/bundlephobia/minzip/zzz-template)](https://bundlephobia.com/package/zzz-template)
7
+ [![license](https://img.shields.io/npm/l/zzz-template.svg)](https://github.com/cuhuak/zzz-template/blob/main/LICENSE)
8
+
9
+ **zzz-template** is an ultra-lightweight JavaScript template engine that leverages native template literals for maximum performance. A fast, hackable alternative to EJS, Handlebars, and Mustache that works in both Node.js and browsers.
10
+
11
+ ## Why zzz-template?
12
+
13
+ | Feature | zzz-template | EJS | Handlebars |
14
+ |---------|-------------|-----|------------|
15
+ | Size (min+gzip) | ~500 bytes | ~6KB | ~17KB |
16
+ | Dependencies | 0 | 1 | 0 |
17
+ | Performance | 24M ops/sec | 247K ops/sec | - |
18
+ | Browser + Node.js | Yes | Yes | Yes |
19
+ | Template Literals | Native | No | No |
20
+
21
+ ## Features
22
+
23
+ - **Echo variables**: `${data.user.name}`
24
+ - **Layouts**: Set layout in child template `${LAYOUT("layout.html")}`, echo content `${data.content}` in `layout.html`
25
+ - **Include (partial) templates**: `${INCLUDE('partial.html', data)}`
26
+ - **Local variables**: `${SET('title', 'Hello world')}`, then use it in template: `${local.title}`
27
+ - **Blazing fast**: Matches vanilla JavaScript performance (24M ops/sec)
28
+ - **Zero dependencies**: No bloat, no supply chain risk
29
+ - **Tiny footprint**: ~50 lines of code, ~500 bytes minified + gzipped
30
+ - **Hackable**: Easy to extend with plugins
31
+ - **Isomorphic**: Works on server (Node.js) and browser
32
+
33
+ ## Installation
34
+
35
+ ```bash
36
+ npm install zzz-template
37
+ ```
38
+
39
+ ### Compile example (see [examples/00-compile](examples/00-compile))
40
+ ``` javascript
41
+ // file examples/00-compile/example.js
42
+ import {ZzzBrowser} from "zzz-template"
43
+
44
+ const zzz = new ZzzBrowser()
45
+ const fn = zzz.compile('Hello ${data.name}') // returns function that renders your template using data: `fn(data)`
46
+ console.log(fn({name: 'Jerry'})); // > "Hello Jerry"
47
+ console.log(fn({name: 'Tom'})); // > "Hello Tom"
48
+ ```
49
+
50
+ ### Basic example (browser), render `<script>` template (see [examples/01-basic](examples/01-basic))
51
+ ``` html
52
+ <!-- file examples/01-basic/page.html -->
53
+ <script id="template" type="plain/text">
54
+ <p>
55
+ Hello ${data.name}
56
+ </p>
57
+ </script>
58
+
59
+ <script type="module">
60
+ import { ZzzBrowser } from '/zzz-template/index.js'
61
+
62
+ const zzz = new ZzzBrowser()
63
+
64
+ const result = zzz.render('template', { name: 'world' })
65
+
66
+ console.log(result)
67
+
68
+ document.body.innerHTML = result
69
+ </script>
70
+ ```
71
+
72
+ ### Basic example (server), render `file` template (see [examples/02-basic](examples/02-basic))
73
+ ``` html
74
+ <!-- file examples/02-basic/template.html -->
75
+ <p>
76
+ Hello ${data.name}
77
+ </p>
78
+ ```
79
+ ``` javascript
80
+ // file examples/02-basic/example.js
81
+ import { ZzzFs } from 'zzz-template/fs.js'
82
+
83
+ const zzz = new ZzzFs({ dir: import.meta.dirname })
84
+ const result = zzz.render('template.html', { name: 'Jerry' })
85
+ console.log(result)
86
+ // OUTPUT:
87
+ // <p>
88
+ // Hello Jerry
89
+ // </p>
90
+ ```
91
+
92
+ ## Include (partial)
93
+ * `useInclude(zzz)` to enable include feature
94
+ * `${INCLUDE('partial', {name: 'universe'})}` to include `partial` template
95
+
96
+ ### Example include (browser) (see [examples/03-include](examples/03-include))
97
+ ``` html
98
+ <!-- file examples/03-include/page.html -->
99
+ <script type="module">
100
+ import { ZzzBrowser, useInclude } from '/zzz-template/index.js'
101
+
102
+ const zzz = new ZzzBrowser()
103
+ useInclude(zzz) // 👈 enables include feature
104
+
105
+ const result = zzz.render('template', { name: 'world' })
106
+
107
+ document.body.innerHTML = result
108
+ </script>
109
+
110
+ <script id="template" type="plain/text">
111
+ <p>
112
+ Hello ${data.name}!
113
+ </p>
114
+
115
+ ${INCLUDE('partial', {name: 'universe'})}
116
+ </script>
117
+
118
+ <script id="partial" type="plain/text">
119
+ <p>
120
+ Hey ${data.name}!
121
+ </p>
122
+ </script>
123
+ ```
124
+
125
+ ## Layouts
126
+ * `useLayout(zzz)` to enable layouts feature
127
+ * `${LAYOUT('layout', {name: 'universe'})}` to set layout in `template`
128
+ * `${data.content}` to echo content (result of `template`) in layout `layout`
129
+
130
+ ### Example Layout (see [examples/06-layout](examples/06-layout))
131
+ ``` html
132
+ <!-- file examples/06-layout/layouts.html -->
133
+ <script type="module">
134
+ import { ZzzBrowser, useLayout } from '/zzz-template/index.js'
135
+
136
+ const zzz = new ZzzBrowser()
137
+ useLayout(zzz) // 👈 enables layout feature
138
+
139
+ const result = zzz.render('template', { name: 'world' })
140
+
141
+ document.body.innerHTML = result
142
+ </script>
143
+
144
+ <script id="template" type="plain/text">
145
+ ${LAYOUT('layout', {name: 'universe'})}
146
+ <p>
147
+ Hello ${data.name}!
148
+ </p>
149
+ </script>
150
+
151
+ <script id="layout" type="plain/text">
152
+ Hey ${data.name}!
153
+ <div>
154
+ ${data.content}
155
+ </div>
156
+ </script>
157
+ ```
158
+
159
+ Layout template itself can set global layout, and global layout can set more global layout, etc.
160
+ See example how layout can include each other: `examples/06-layout/layouts2.html`. Please note the example also uses local vars feature.
161
+
162
+ ### Example Layout Advanced (see [examples/06-layout](examples/06-layout))
163
+ ``` html
164
+ <!-- file examples/06-layout/layouts2.html -->
165
+ <script id="template" type="plain/text">
166
+ ${LAYOUT('layout')}
167
+ ${SET('title', 'My page')}
168
+ <p>
169
+ Hello ${data.name}
170
+ </p>
171
+ </script>
172
+
173
+ <script id="layout" type="plain/text">
174
+ ${LAYOUT('global')}
175
+ <div style="background: #eee; padding: 1em; margin: 1em 0;">
176
+ <div>layout begin</div>
177
+ <section class="layout">
178
+ ${data.content}
179
+ </section>
180
+ <div>layout end</div>
181
+ </div>
182
+ </script>
183
+
184
+ <script id="global" type="plain/text">
185
+ <div style="background: #ffb; padding: 1em;">
186
+ <nav>
187
+ <a href="#">link1</a>
188
+ <a href="#">link2</a>
189
+ <a href="#">link3</a>
190
+ </nav>
191
+ <h1>${local.title}</h1>
192
+ ${data.content}
193
+ <footer>Footer</footer>
194
+ </div>
195
+ </script>
196
+
197
+ <script type="module">
198
+ import { ZzzBrowser, useLayout, useLocal } from '/zzz-template/index.js'
199
+
200
+ const zzz = new ZzzBrowser()
201
+ useLayout(zzz) // 👈 enables layout feature
202
+ useLocal(zzz) // 👈 enables local vars feature
203
+
204
+ const result = zzz.render('template', { name: 'world' })
205
+
206
+ console.log(result)
207
+
208
+ document.body.innerHTML = result
209
+ </script>
210
+ ```
211
+
212
+ ## Conditions IF
213
+ * `useIfMap(zzz)` to enable "if, each" feature
214
+ * `${IF(data.n === 42, 'Hello ${data.name}!', {name: 'world'})}` to echo string on condition
215
+ * you may want to pass data into template
216
+
217
+ ### Example Condition IF (see [examples/04-if](examples/04-if))
218
+ ``` html
219
+ <!-- file examples/04-if/if.html -->
220
+ <script type="module">
221
+ import { ZzzBrowser, useIfMap } from '/zzz-template/index.js'
222
+
223
+ const zzz = new ZzzBrowser()
224
+ useIfMap(zzz) // 👈 enables "if/map" feature
225
+
226
+ const result = zzz.render('template', { name: 'world', n: 42 })
227
+
228
+ document.body.innerHTML = result
229
+ </script>
230
+
231
+ <script id="template" type="plain/text">
232
+ <p>
233
+ Hello ${data.name}!
234
+ </p>
235
+
236
+ ${IF(data.n === 42, 'Hello ${data.n}!', {n: data.n})}
237
+
238
+ <hr>
239
+
240
+ ${IF(data.n === 42, `
241
+ Hello ${data.n}! <br>
242
+ ${IF(data.n % 2 === 0, `
243
+ doubled ${data.n} is \${data.number}.
244
+ `, {number: data.n * 2})}
245
+ `)}
246
+ </script>
247
+ ```
248
+
249
+ ## Conditions IFI
250
+ * `useIfMap(zzz)` to enable "if, each" feature
251
+ * `${IFI(condition, 'template', data)}`: if `condition` then include `template` with `data`
252
+ * do not forget to pass data
253
+
254
+ ### Example Condition IFI: `if (condition) include` (see [examples/04-if](examples/04-if))
255
+ ``` html
256
+ <!-- file examples/04-if/ifi.html -->
257
+ <script type="module">
258
+ import { ZzzBrowser, useIfMap } from '/zzz-template/index.js'
259
+
260
+ const zzz = new ZzzBrowser()
261
+ useIfMap(zzz) // 👈 enables "if/map" feature
262
+
263
+ const result = zzz.render('template', { name: 'world', n: 42 })
264
+
265
+ document.body.innerHTML = result
266
+ </script>
267
+
268
+ <script id="template" type="plain/text">
269
+ <p>
270
+ Hello ${data.name}!
271
+ </p>
272
+
273
+ ${IFI(data.n % 2 == 0, 'partial', {n: data.n})}
274
+ </script>
275
+
276
+ <script id="partial" type="plain/text">
277
+ <p>
278
+ ${data.n} is even!
279
+ </p>
280
+ </script>
281
+ ```
282
+
283
+ ## Iterate, loop, map, for (include template for elements)
284
+ * `useIfMap(zzz)` to enable "if, each" feature
285
+ * `${MAP('template string', elements)}`: for each el in elements render `template string` w/ el as data
286
+
287
+ ### Example MAP (see [examples/05-map](examples/05-map))
288
+ ``` html
289
+ <!-- file examples/05-map/map.html -->
290
+ <script type="module">
291
+ import { ZzzBrowser, useIfMap } from '/zzz-template/index.js'
292
+
293
+ const zzz = new ZzzBrowser()
294
+ useIfMap(zzz) // 👈 enables "if/map" feature
295
+
296
+ const pets = [{ name: 'cat', say: 'meow' }, { name: 'dog', say: 'woof' }]
297
+ const result = zzz.render('template', { name: 'John Doe', pets })
298
+
299
+ document.body.innerHTML = result
300
+ </script>
301
+
302
+ <script id="template" type="plain/text">
303
+ // [1] using javascript .map
304
+ <ul>
305
+ ${data.pets.map(x => TEMPLATE('<li>${data.name}</li>', x)).join('')}
306
+ </ul>
307
+ // [2] using MAP and string
308
+ <ul>
309
+ ${MAP(data.pets, '<li>${data.name}</li>')}
310
+ </ul>
311
+ // [2'] using MAP and string template (note that dollar-sign ($) is escaped)
312
+ <ul>
313
+ ${MAP(data.pets, `<li>\${data.name}</li>`)}
314
+ </ul>
315
+ // [3] using MAPI (map include) to include template for each element
316
+ <ul>
317
+ ${MAPI(data.pets, 'pet')}
318
+ </ul>
319
+ </script>
320
+
321
+ <script id="pet" type="plain/text">
322
+ <li>${data.name} (says: ${data.say})</li>
323
+ </script>
324
+ ```
325
+
326
+ ## Extend and hack
327
+ ZzzTemplate already has a few built-in plugins. A plugin is just a function that monkey-patches the ZzzTemplate instance.
328
+ For instance, you can inject your code into the compile function. Here is a 'trim' example:
329
+
330
+ ``` javascript
331
+ import {ZzzBrowser, useContentTrim} from "zzz-template"
332
+
333
+ const zzz = new ZzzBrowser()
334
+ useContentTrim(zzz)
335
+ const fn = zzz.compile(' Hello ${data.name} ')
336
+ // note that result is trimmed
337
+ const result = fn({name: 'Tom'})
338
+ console.log(result); // > "Hello Tom"
339
+ ```
340
+ Here is the code of `useContentTrim` (built-in)
341
+ ``` javascript
342
+ function useContentTrim(zzz) {
343
+ zzz.e.push('content = content.trim();')
344
+ }
345
+ ```
346
+ This function pushes a code snippet to the end array that will be invoked after the template content is compiled.
347
+
348
+
349
+ Or you may want to introduce a new var in your templates.
350
+
351
+ ``` javascript
352
+ import {ZzzBrowser, useContentTrim} from "zzz-template"
353
+
354
+ const zzz = new ZzzBrowser()
355
+
356
+ // zzz.s -- s means start (before template content compiled)
357
+ zzz.s.push('let $$ = data;') // introduce `$$` for `data`
358
+ // zzz.e -- e means end (after template content compiled)
359
+ zzz.e.push('content = content.trim();')
360
+ const fn = zzz.compile(' Hello ${$$.name} ') // we use new name `$$` for `data`
361
+ const result = fn({name: 'Tom'})
362
+ console.log(result); // > "Hello Tom"
363
+ ```
364
+
365
+ ### Example that introduces `ESCAPE` function that escapes string (see [examples/10-extend](examples/10-extend))
366
+ ``` html
367
+ <!-- file examples/10-extend/escape.html -->
368
+ <script type="module">
369
+ import { ZzzBrowser, useFn } from '/zzz-template/index.js'
370
+
371
+ const zzz = new ZzzBrowser()
372
+
373
+ function escapeHtml (unsafe) {
374
+ return unsafe
375
+ .replaceAll("&", "&amp;")
376
+ .replaceAll("<", "&lt;")
377
+ .replaceAll(">", "&gt;")
378
+ .replaceAll('"', "&quot;")
379
+ .replaceAll("'", "&#039;")
380
+ }
381
+
382
+ useFn(zzz, escapeHtml, 'ESCAPE')
383
+ const evilString = 'John<img src="" onerror=alert("Boo!")>'
384
+
385
+ const result = zzz.render('template', { name: evilString })
386
+
387
+ console.log(result)
388
+ document.body.innerHTML = result
389
+ </script>
390
+
391
+ <script id="template" type="plain/text">
392
+ <p>
393
+ Hello ${ESCAPE(data.name)}!
394
+ </p>
395
+ </script>
396
+ ```
397
+
398
+ ## Fast
399
+
400
+ Fastest JS engine ever :) That is true, see the benchmark results (ran on the author's old Intel i7):
401
+
402
+ ```
403
+ --------- Benchmark Render ---------
404
+ vanilla render x 24,478,910 ops/sec ±1.23% (91 runs sampled)
405
+ zzz render x 24,256,470 ops/sec ±1.25% (90 runs sampled)
406
+ literal render x 16,843,920 ops/sec ±1.63% (89 runs sampled)
407
+ zup render x 2,738,409 ops/sec ±1.43% (91 runs sampled)
408
+ ejs render x 247,632 ops/sec ±2.10% (91 runs sampled)
409
+ dot render x 1,096,741 ops/sec ±0.58% (93 runs sampled)
410
+ edge render x 8,037 ops/sec ±1.80% (90 runs sampled)
411
+ Fastest is vanilla render, zzz render
412
+ ```
413
+
414
+ Try to run benchmarks
415
+ ```
416
+ # go to bench
417
+ cd bench
418
+
419
+ # install deps
420
+ npm i
421
+
422
+ # run
423
+ npm run bench
424
+ ```
425
+
426
+ ## Security
427
+ Do not render templates (by filename) that come from user input, or values in templates that come from user input—it is dangerous.
428
+ And if you do, please make sure you:
429
+ - provide a secure `zzz.read` function that reads files from a specified directory, not `../../../secret.passwords`
430
+ - escape all user input to prevent XSS attacks
431
+
432
+ ## License
433
+ MIT
434
+
435
+ ## Related
436
+
437
+ Looking for JavaScript template engines? Here are some alternatives:
438
+ - [EJS](https://www.npmjs.com/package/ejs) - Embedded JavaScript templates
439
+ - [Handlebars](https://www.npmjs.com/package/handlebars) - Semantic templates
440
+ - [Mustache](https://www.npmjs.com/package/mustache) - Logic-less templates
441
+ - [doT](https://www.npmjs.com/package/dot) - Fast template engine
442
+
443
+ ---
444
+ Docs revision: 2025-11-24T08:05:23.013Z
package/fs.d.ts ADDED
@@ -0,0 +1,13 @@
1
+ import { ZzzBase } from "./index.js";
2
+
3
+ export * from './index.js';
4
+
5
+ export interface ZzzFsOptions extends Record<string, any> {
6
+ dir?: string;
7
+ }
8
+
9
+ export class ZzzFs extends ZzzBase {
10
+ _dir: string;
11
+ constructor($this?: ZzzFsOptions);
12
+ read(f: string): string;
13
+ }
package/fs.js ADDED
@@ -0,0 +1,15 @@
1
+ import {readFileSync} from 'node:fs'
2
+ import {join} from "node:path"
3
+ import {ZzzBase} from "./index.js"
4
+
5
+ export * from './index.js'
6
+
7
+ export class ZzzFs extends ZzzBase {
8
+ constructor($this = {}) {
9
+ super($this)
10
+ this._dir = $this.dir || ''
11
+ }
12
+ read (f) {
13
+ return readFileSync(join(this._dir, f), "utf8")
14
+ }
15
+ }
package/index.d.ts ADDED
@@ -0,0 +1,20 @@
1
+ export class ZzzBase {
2
+ s: string[];
3
+ e: string[];
4
+ $: Record<string, any>;
5
+ constructor($this?: Record<string, any>);
6
+ compile(str: string, local?: Record<string, any>, sign?: string): (data: any, parent?: any) => string;
7
+ render(template: string, data: any, local?: Record<string, any>): string;
8
+ read(f: string): string;
9
+ }
10
+
11
+ export class ZzzBrowser extends ZzzBase {
12
+ read(f: string): string;
13
+ }
14
+
15
+ export function useFn(zzz: ZzzBase, fn: Function, alias?: string | false): void;
16
+ export function useContentTrim(zzz: ZzzBase): void;
17
+ export function useInclude(zzz: ZzzBase, alias?: string): void;
18
+ export function useLayout(zzz: ZzzBase, alias?: string): void;
19
+ export function useLocal(zzz: ZzzBase, aliasSet?: string, aliasSeta?: string): void;
20
+ export function useIfMap(zzz: ZzzBase, aliases?: boolean): void;
package/index.js ADDED
@@ -0,0 +1,49 @@
1
+ export class ZzzBase {
2
+ s = [] // s - start, before content
3
+ e = [] // e - end, after content
4
+ constructor($this = {}) {
5
+ this.$ = $this
6
+ }
7
+ compile(str, local, sign = 'data, parent') {
8
+ const fn = new Function(sign, `${this.s.join(';')}let content=\`${str}\`;${this.e.join(';')}return content;`)
9
+ return fn.bind({...this.$, local}) // shallow copy of $
10
+ }
11
+ render(template, data, local = {}) {
12
+ return this.compile(this.read(template), {...local})(data) // shallow copy of local
13
+ }
14
+ read(f) {} // must be implemented
15
+ }
16
+ export class ZzzBrowser extends ZzzBase {
17
+ read (f) {
18
+ return window.document.getElementById(f).innerText
19
+ }
20
+ }
21
+ export function useFn(zzz, fn, alias) {
22
+ zzz.$[fn.name] = fn
23
+ if (alias) zzz.s.push(`let ${alias} = this.${fn.name}.bind(this);`)
24
+ }
25
+ export function useContentTrim(zzz) {
26
+ zzz.e.push('content = content.trim();')
27
+ }
28
+ export function useInclude(zzz, alias = 'INCLUDE') {
29
+ useFn(zzz, function include(file, data) {return zzz.compile(zzz.read(file), this.local)(data)}, alias)
30
+ }
31
+ export function useLayout(zzz, alias = 'LAYOUT') {
32
+ zzz.$['include'] || useInclude(zzz)
33
+ zzz.e.push('if(this._layout)return this.include(this._layout.t,{...this._layout.d,content});')
34
+ useFn(zzz, function layout(t, d) {this._layout={t,d};return ''}, alias)
35
+ }
36
+ export function useLocal(zzz, aliasSet = 'SET', aliasSeta = 'SETA') {
37
+ zzz.s.push('let local = this.local;')
38
+ zzz.$.local = {}
39
+ useFn(zzz, function set(key, values) {this.local[key] = values;return ''}, aliasSet)
40
+ useFn(zzz, function seta(key, ...values) {this.local[key] = [(this.local[key] ?? []), ...values].flat();return ''}, aliasSeta)
41
+ }
42
+ export function useIfMap(zzz, aliases = true) {
43
+ zzz.$['include'] || useInclude(zzz)
44
+ useFn(zzz, function template(str, data) {return zzz.compile(str, this.local)(data)}, aliases && 'TEMPLATE')
45
+ useFn(zzz, function if_template(cond, str, data) {return cond ? zzz.compile(str, this.local)(data) : ''}, aliases && 'IF')
46
+ useFn(zzz, function if_include(cond, file, data) {return cond ? this.include(file, data) : ''}, aliases && 'IFI')
47
+ useFn(zzz, function map_template(arr, str) {return arr.map(x => {return zzz.compile(str, this.local)(x)}).join('')}, aliases && 'MAP')
48
+ useFn(zzz, function map_include(arr, file) {return arr.map(x => {return this.include(file, x)}).join('')}, aliases && 'MAPI')
49
+ }
package/package.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "zzz-template",
3
+ "version": "0.9.1",
4
+ "type": "module",
5
+ "description": "Fastest JavaScript template engine using native template literals. Zero dependencies, ~500 bytes, works in Node.js and browser. EJS/Handlebars alternative.",
6
+ "author": {
7
+ "name": "Evgeny Sinitsyn",
8
+ "email": "cuhuak@gmail.com"
9
+ },
10
+ "keywords": [
11
+ "template",
12
+ "template-engine",
13
+ "template-literal",
14
+ "template-literals",
15
+ "javascript-template",
16
+ "html-template",
17
+ "render",
18
+ "layout",
19
+ "partial",
20
+ "include",
21
+ "fast",
22
+ "lightweight",
23
+ "zero-dependency",
24
+ "browser",
25
+ "nodejs",
26
+ "isomorphic",
27
+ "ejs-alternative",
28
+ "handlebars-alternative",
29
+ "mustache-alternative",
30
+ "string-interpolation"
31
+ ],
32
+ "license": "MIT",
33
+ "repository": {
34
+ "type": "git",
35
+ "url": "git+https://github.com/cuhuak/zzz-template.git"
36
+ },
37
+ "bugs": {
38
+ "url": "https://github.com/cuhuak/zzz-template/issues"
39
+ },
40
+ "homepage": "https://github.com/cuhuak/zzz-template#readme",
41
+ "main": "index.js",
42
+ "types": "index.d.ts",
43
+ "files": [
44
+ "index.js",
45
+ "index.d.ts",
46
+ "fs.js",
47
+ "fs.d.ts",
48
+ "readme.md",
49
+ "LICENSE"
50
+ ],
51
+ "engines": {
52
+ "node": ">=18.0.0"
53
+ },
54
+ "exports": {
55
+ ".": {
56
+ "types": "./index.d.ts",
57
+ "import": "./index.js",
58
+ "require": "./index.js"
59
+ },
60
+ "./fs.js": {
61
+ "types": "./fs.d.ts",
62
+ "import": "./fs.js",
63
+ "require": "./fs.js"
64
+ }
65
+ }
66
+ }