lite-template 0.1.0 → 0.1.2
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/README.md +17 -1
- package/dist/index.d.ts +29 -3
- package/dist/index.js +87 -6
- package/package.json +10 -1
package/README.md
CHANGED
|
@@ -23,7 +23,7 @@ To prioritize performance and maintain its minimalist footprint (<10KB), `lite-t
|
|
|
23
23
|
|
|
24
24
|
1. **Strictly Logic-Driven**: Unlike EJS, it does not include a complex built-in caching layer. Reusable templates should be pre-compiled using `compile()` at the application level.
|
|
25
25
|
2. **Explicit Scope**: Uses the `with` block for performance. Variables must be defined within the provided `data` object to be accessible; it does not automatically pull from global scope.
|
|
26
|
-
3. **
|
|
26
|
+
3. **Include System**: A native `include()` function works out-of-the-box (just like EJS) when `options.filename` is provided to `render()`, managing recursive resolution automatically. Does not implement `<%- layout() %>` systems.
|
|
27
27
|
4. **No Middleware Integration**: This is a pure string-to-HTML engine—no native Express.js view-engine integration is included out of the box.
|
|
28
28
|
5. **ESM Only**: Built exclusively for modern ESM toolchains.
|
|
29
29
|
|
|
@@ -76,6 +76,22 @@ const html = await render(template, {
|
|
|
76
76
|
});
|
|
77
77
|
```
|
|
78
78
|
|
|
79
|
+
### Native Built-in Includes
|
|
80
|
+
|
|
81
|
+
When you pass `filename` in the options, `lite-template` automatically exposes a built-in cross-file `include()` resolver.
|
|
82
|
+
|
|
83
|
+
```javascript
|
|
84
|
+
// page.ejs
|
|
85
|
+
// <h1>My Page</h1>
|
|
86
|
+
// <%- await include('footer', { text: "Copyright" }) %>
|
|
87
|
+
|
|
88
|
+
const html = await render(
|
|
89
|
+
'<%- await include("footer") %>',
|
|
90
|
+
{ globalVar: true },
|
|
91
|
+
{ filename: '/path/to/page.ejs' }
|
|
92
|
+
);
|
|
93
|
+
```
|
|
94
|
+
|
|
79
95
|
### First-Class Async/Await
|
|
80
96
|
|
|
81
97
|
```javascript
|
package/dist/index.d.ts
CHANGED
|
@@ -1,12 +1,38 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Compiles an EJS-style template string into an async function.
|
|
3
|
-
* Supports: <% js %>, <%= escaped %>, <%- unescaped %>
|
|
3
|
+
* Supports: <% js %>, <%= escaped %>, <%- unescaped %>, <%# comment %>
|
|
4
4
|
*/
|
|
5
5
|
export declare function compile(template: any): (data: any) => any;
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
7
|
+
* Renders an EJS-style template string with data.
|
|
8
|
+
*
|
|
9
|
+
* Provides a built-in `include()` function when `options.filename` is set,
|
|
10
|
+
* enabling file-based template inclusion just like EJS.
|
|
11
|
+
*
|
|
12
|
+
* If the caller supplies their own `include` in `data`, it takes precedence
|
|
13
|
+
* over the built-in version (allowing frameworks like docmd to extend behavior).
|
|
14
|
+
*
|
|
15
|
+
* @param template - The EJS template string to render
|
|
16
|
+
* @param data - Data object accessible inside the template via `with(data)`
|
|
17
|
+
* @param options - Options: `filename` (path of this template, enables include resolution)
|
|
18
|
+
* @returns The rendered string
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```js
|
|
22
|
+
* import tpl from 'lite-template';
|
|
23
|
+
*
|
|
24
|
+
* // Simple render
|
|
25
|
+
* const html = await tpl.render('<h1><%= title %></h1>', { title: 'Hello' });
|
|
26
|
+
*
|
|
27
|
+
* // With file-based includes
|
|
28
|
+
* const html = await tpl.render(
|
|
29
|
+
* '<%- include("header") %><p>Body</p>',
|
|
30
|
+
* { siteName: 'My Site' },
|
|
31
|
+
* { filename: '/path/to/current/template.ejs' }
|
|
32
|
+
* );
|
|
33
|
+
* ```
|
|
8
34
|
*/
|
|
9
|
-
export declare function render(template: any, data?: {}, options?:
|
|
35
|
+
export declare function render(template: any, data?: {}, options?: any): Promise<any>;
|
|
10
36
|
declare const _default: {
|
|
11
37
|
render: typeof render;
|
|
12
38
|
compile: typeof compile;
|
package/dist/index.js
CHANGED
|
@@ -9,7 +9,7 @@ function escapeHtml(str) {
|
|
|
9
9
|
}
|
|
10
10
|
/**
|
|
11
11
|
* Compiles an EJS-style template string into an async function.
|
|
12
|
-
* Supports: <% js %>, <%= escaped %>, <%- unescaped %>
|
|
12
|
+
* Supports: <% js %>, <%= escaped %>, <%- unescaped %>, <%# comment %>
|
|
13
13
|
*/
|
|
14
14
|
export function compile(template) {
|
|
15
15
|
let code = "";
|
|
@@ -50,7 +50,7 @@ export function compile(template) {
|
|
|
50
50
|
fn = new AsyncFunction("__data", "escapeHtml", wrappedCode);
|
|
51
51
|
}
|
|
52
52
|
catch (e) {
|
|
53
|
-
console.error("COMPILATION ERROR CODE:\n" + wrappedCode.split('
|
|
53
|
+
console.error("COMPILATION ERROR CODE:\n" + wrappedCode.split('\n').map((l, i) => `${i + 1}: ${l}`).join('\n'));
|
|
54
54
|
throw e;
|
|
55
55
|
}
|
|
56
56
|
return function (data) {
|
|
@@ -58,13 +58,94 @@ export function compile(template) {
|
|
|
58
58
|
};
|
|
59
59
|
}
|
|
60
60
|
/**
|
|
61
|
-
*
|
|
61
|
+
* Built-in include function for file-based template inclusion.
|
|
62
|
+
* Reads a file relative to the current template's location, compiles and renders it.
|
|
63
|
+
*
|
|
64
|
+
* This provides the core EJS-compatible `include()` behavior:
|
|
65
|
+
* - Resolves paths relative to the current template's `filename`
|
|
66
|
+
* - Auto-appends `.ejs` extension if missing
|
|
67
|
+
* - Recursively renders included templates
|
|
68
|
+
* - Merges parent data with include-specific data
|
|
69
|
+
*
|
|
70
|
+
* Consumers can override this by passing their own `include` function in `data`.
|
|
71
|
+
*
|
|
72
|
+
* @param parentFilename - Absolute path of the template doing the including
|
|
73
|
+
* @param parentData - Data context from the parent template
|
|
74
|
+
* @param options - Render options (passed through to recursive render calls)
|
|
75
|
+
*/
|
|
76
|
+
async function builtinInclude(parentFilename, parentData, options, name, includeData = {}) {
|
|
77
|
+
const extName = !name.endsWith('.ejs') ? name + '.ejs' : name;
|
|
78
|
+
// Custom includer support (e.g. for virtual file systems or memory templates)
|
|
79
|
+
if (options.includer) {
|
|
80
|
+
const res = options.includer(extName, parentFilename);
|
|
81
|
+
if (res && res.template) {
|
|
82
|
+
const mergedData = { ...parentData, ...includeData };
|
|
83
|
+
delete mergedData.include;
|
|
84
|
+
return await render(res.template, mergedData, { ...options, filename: res.filename || parentFilename });
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// Lazy-load Node.js modules (keeps the package usable in non-Node environments at compile time)
|
|
88
|
+
const { promises: fsPromises } = await import('node:fs');
|
|
89
|
+
const path = await import('node:path');
|
|
90
|
+
let targetPath;
|
|
91
|
+
if (parentFilename) {
|
|
92
|
+
targetPath = path.resolve(path.dirname(parentFilename), extName);
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
targetPath = path.resolve(extName);
|
|
96
|
+
}
|
|
97
|
+
let content = await fsPromises.readFile(targetPath, 'utf8');
|
|
98
|
+
// Custom preprocessor hook before rendering (useful for stripping syntax outside of the engine's concern)
|
|
99
|
+
if (options.preprocessor) {
|
|
100
|
+
content = options.preprocessor(content, targetPath);
|
|
101
|
+
}
|
|
102
|
+
const mergedData = { ...parentData, ...includeData };
|
|
103
|
+
// Remove the parent's include fn — render() will create a new one scoped to the new file
|
|
104
|
+
delete mergedData.include;
|
|
105
|
+
return await render(content, mergedData, { ...options, filename: targetPath });
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Renders an EJS-style template string with data.
|
|
109
|
+
*
|
|
110
|
+
* Provides a built-in `include()` function when `options.filename` is set,
|
|
111
|
+
* enabling file-based template inclusion just like EJS.
|
|
112
|
+
*
|
|
113
|
+
* If the caller supplies their own `include` in `data`, it takes precedence
|
|
114
|
+
* over the built-in version (allowing frameworks like docmd to extend behavior).
|
|
115
|
+
*
|
|
116
|
+
* @param template - The EJS template string to render
|
|
117
|
+
* @param data - Data object accessible inside the template via `with(data)`
|
|
118
|
+
* @param options - Options: `filename` (path of this template, enables include resolution)
|
|
119
|
+
* @returns The rendered string
|
|
120
|
+
*
|
|
121
|
+
* @example
|
|
122
|
+
* ```js
|
|
123
|
+
* import tpl from 'lite-template';
|
|
124
|
+
*
|
|
125
|
+
* // Simple render
|
|
126
|
+
* const html = await tpl.render('<h1><%= title %></h1>', { title: 'Hello' });
|
|
127
|
+
*
|
|
128
|
+
* // With file-based includes
|
|
129
|
+
* const html = await tpl.render(
|
|
130
|
+
* '<%- include("header") %><p>Body</p>',
|
|
131
|
+
* { siteName: 'My Site' },
|
|
132
|
+
* { filename: '/path/to/current/template.ejs' }
|
|
133
|
+
* );
|
|
134
|
+
* ```
|
|
62
135
|
*/
|
|
63
136
|
export async function render(template, data = {}, options = {}) {
|
|
64
|
-
// Allow caching if 'filename' or 'id' is passed usually, but here we just compile and run.
|
|
65
137
|
const fn = compile(template);
|
|
66
|
-
|
|
67
|
-
|
|
138
|
+
const filename = options.filename || data.__filename || null;
|
|
139
|
+
const enriched = {
|
|
140
|
+
locals: data,
|
|
141
|
+
...data,
|
|
142
|
+
__filename: filename
|
|
143
|
+
};
|
|
144
|
+
// Provide built-in include() if filename OR a virtual includer is set AND the caller hasn't provided their own
|
|
145
|
+
if ((filename || options.includer) && !enriched.include) {
|
|
146
|
+
enriched.include = (name, includeData = {}) => builtinInclude(filename, enriched, options, name, includeData);
|
|
147
|
+
}
|
|
148
|
+
return await fn(enriched);
|
|
68
149
|
}
|
|
69
150
|
export default {
|
|
70
151
|
render,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lite-template",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Ultra lightweight, zero-dependency async template engine compatible with basic EJS syntax.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -8,10 +8,19 @@
|
|
|
8
8
|
],
|
|
9
9
|
"main": "dist/index.js",
|
|
10
10
|
"types": "dist/index.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"import": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"default": "./dist/index.js"
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
},
|
|
11
19
|
"scripts": {
|
|
12
20
|
"build": "tsc"
|
|
13
21
|
},
|
|
14
22
|
"devDependencies": {
|
|
23
|
+
"@types/node": "^25.5.2",
|
|
15
24
|
"typescript": "^5.0.0"
|
|
16
25
|
},
|
|
17
26
|
"keywords": [
|