mikel 0.33.0 → 0.34.0
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 +116 -7
- package/index.d.ts +25 -4
- package/index.js +48 -6
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -25,11 +25,41 @@ Mikel supports the following syntax for rendering templates:
|
|
|
25
25
|
|
|
26
26
|
Use double curly braces `{{ }}` to insert variables into your template. Variables will be replaced with the corresponding values from the data object.
|
|
27
27
|
|
|
28
|
+
```javascript
|
|
29
|
+
const result = m(`Hello {{name}}!`, { name: "World" });
|
|
30
|
+
// Output: 'Hello World!'
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
#### Nested values
|
|
34
|
+
|
|
35
|
+
You can access nested properties of an object using dot notation.
|
|
36
|
+
|
|
37
|
+
```javascript
|
|
38
|
+
const result = m(`Hello {{user.name}}!`, {
|
|
39
|
+
user: { name: "John" },
|
|
40
|
+
});
|
|
41
|
+
// Output: 'Hello John!'
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
#### Array values
|
|
45
|
+
|
|
46
|
+
You can access a specific element of an array using its index in dot notation.
|
|
47
|
+
|
|
48
|
+
```javascript
|
|
49
|
+
const result = m(`Hello {{users.0.name}}!`, {
|
|
50
|
+
users: [
|
|
51
|
+
{ name: "John" },
|
|
52
|
+
{ name: "Alice" },
|
|
53
|
+
],
|
|
54
|
+
});
|
|
55
|
+
// Output: 'Hello John!'
|
|
56
|
+
```
|
|
57
|
+
|
|
28
58
|
#### Fallback values
|
|
29
59
|
|
|
30
60
|
> Added in `v0.14.0`.
|
|
31
61
|
|
|
32
|
-
You can specify a value
|
|
62
|
+
You can specify a fallback value using the `||` operator. This value will be used when the variable is not defined or is empty.
|
|
33
63
|
|
|
34
64
|
```javascript
|
|
35
65
|
const result = m(`Hello {{name || "World"}}!`, {});
|
|
@@ -700,9 +730,6 @@ const result = m("Users: {{=fullName ...user1}} and {{=fullName ...user2}}", dat
|
|
|
700
730
|
console.log(result); // --> "Users: John Doe and Alice Smith"
|
|
701
731
|
```
|
|
702
732
|
|
|
703
|
-
Of course, Jose — here’s a version of the **Subexpressions** documentation written to perfectly match the tone, structure, and formatting conventions of the current README.
|
|
704
|
-
It follows the same patterns: short intro, version note, examples, concise explanations, no extra fluff.
|
|
705
|
-
|
|
706
733
|
### Subexpressions
|
|
707
734
|
|
|
708
735
|
> Added in `v0.30.0`.
|
|
@@ -814,15 +841,31 @@ It also exposes the following additional methods:
|
|
|
814
841
|
|
|
815
842
|
> Added in `v0.19.0`.
|
|
816
843
|
|
|
817
|
-
|
|
818
|
-
|
|
844
|
+
Extends the instance with additional helpers, functions, partials, or hooks. Accepts either an options object or a plugin function.
|
|
845
|
+
|
|
846
|
+
When called with an **object**, it registers the provided helpers, functions, and partials directly:
|
|
847
|
+
|
|
819
848
|
```javascript
|
|
820
849
|
mk.use({
|
|
850
|
+
helpers: {
|
|
851
|
+
uppercase: ({ fn, data }) => fn(data).toUpperCase(),
|
|
852
|
+
},
|
|
821
853
|
partials: {
|
|
822
|
-
foo: "
|
|
854
|
+
foo: "Hello {{name}}!",
|
|
823
855
|
},
|
|
824
856
|
});
|
|
825
857
|
```
|
|
858
|
+
|
|
859
|
+
When called with a **function**, the function receives the internal context of the instance, giving full access to the hooks system and all registered helpers, functions, and partials. This is the recommended approach for writing reusable plugins:
|
|
860
|
+
|
|
861
|
+
```javascript
|
|
862
|
+
const myPlugin = (ctx) => {
|
|
863
|
+
ctx.hooks.add("preRender", template => template.trim());
|
|
864
|
+
ctx.hooks.add("postRender", output => output.trim());
|
|
865
|
+
};
|
|
866
|
+
|
|
867
|
+
mk.use(myPlugin);
|
|
868
|
+
```
|
|
826
869
|
|
|
827
870
|
#### `mk.addHelper(helperName, helperFn)`
|
|
828
871
|
|
|
@@ -880,6 +923,72 @@ This function converts special HTML characters `&`, `<`, `>`, `"`, and `'` to th
|
|
|
880
923
|
|
|
881
924
|
This function returns the value in `object` following the provided `path` string.
|
|
882
925
|
|
|
926
|
+
## Advanced
|
|
927
|
+
|
|
928
|
+
### Built‑in Plugins
|
|
929
|
+
|
|
930
|
+
> Added in `v0.34.0`.
|
|
931
|
+
|
|
932
|
+
Mikel includes a small set of built‑in plugins that provide common functionality without requiring additional packages. These plugins integrate directly into the compilation lifecycle and use the same hook system available to any external plugin.
|
|
933
|
+
|
|
934
|
+
#### `mikel.WrapperPlugin(options)`
|
|
935
|
+
|
|
936
|
+
Wraps the original template by inserting custom text before and/or after it. The wrapper is applied before rendering, which means it can contain mikel expressions (`{{variables}}`, helpers, etc.).
|
|
937
|
+
|
|
938
|
+
```javascript
|
|
939
|
+
mk.use(mikel.WrapperPlugin({
|
|
940
|
+
header: "<!-- START -->\n",
|
|
941
|
+
footer: "\n<!-- END -->",
|
|
942
|
+
}));
|
|
943
|
+
```
|
|
944
|
+
|
|
945
|
+
#### `mikel.StatePlugin(state)`
|
|
946
|
+
|
|
947
|
+
Defines static state variables that become available inside templates through the `@variable` syntax. These values are merged into the initial state before rendering.
|
|
948
|
+
|
|
949
|
+
```javascript
|
|
950
|
+
mk.use(mikel.StatePlugin({
|
|
951
|
+
version: "1.0.0",
|
|
952
|
+
environment: "development",
|
|
953
|
+
}));
|
|
954
|
+
```
|
|
955
|
+
|
|
956
|
+
### Hooks
|
|
957
|
+
|
|
958
|
+
> Added in `v0.34.0`.
|
|
959
|
+
|
|
960
|
+
Hooks allows plugins to tap into different stages of the rendering pipeline. Hooks are registered using the `hooks.add` method inside a plugin function passed to `mk.use()`.
|
|
961
|
+
|
|
962
|
+
```javascript
|
|
963
|
+
mk.use((context) => {
|
|
964
|
+
context.hooks.add("someHook", (params) => {
|
|
965
|
+
// ...
|
|
966
|
+
});
|
|
967
|
+
});
|
|
968
|
+
```
|
|
969
|
+
|
|
970
|
+
Available hooks:
|
|
971
|
+
|
|
972
|
+
| Hook | Type | Receives | Must return | Description |
|
|
973
|
+
|------|-------|----------|-------------|-------------|
|
|
974
|
+
| `preRender` | Waterfall | `template` (string) | `template` (string) | Called before rendering. The returned value is used as the template. |
|
|
975
|
+
| `postRender` | Waterfall | `output` (string) | `output` (string) | Called after rendering. The returned value is used as the final output. |
|
|
976
|
+
|
|
977
|
+
Example:
|
|
978
|
+
|
|
979
|
+
```javascript
|
|
980
|
+
const myPlugin = (context) => {
|
|
981
|
+
// trim the template before rendering
|
|
982
|
+
context.hooks.add("preRender", template => template.trim());
|
|
983
|
+
// minify the output after rendering
|
|
984
|
+
context.hooks.add("postRender", output => output.replace(/\s+/g, " ").trim());
|
|
985
|
+
};
|
|
986
|
+
|
|
987
|
+
mk.use(myPlugin);
|
|
988
|
+
|
|
989
|
+
console.log(mk(" Hello {{name}}! ", { name: "World" })); // --> "Hello World!"
|
|
990
|
+
```
|
|
991
|
+
|
|
883
992
|
## License
|
|
884
993
|
|
|
885
994
|
This project is licensed under the [MIT License](LICENSE).
|
package/index.d.ts
CHANGED
|
@@ -24,26 +24,45 @@ export type MikelFunction = (params: {
|
|
|
24
24
|
state: Record<string, any>;
|
|
25
25
|
}) => string | void;
|
|
26
26
|
|
|
27
|
+
export type MikelState = Record<string, string>;
|
|
28
|
+
|
|
27
29
|
export type MikelOptions = {
|
|
28
30
|
helpers?: Record<string, MikelHelper>;
|
|
29
31
|
partials?: Record<string, string | MikelPartial>;
|
|
30
32
|
functions?: Record<string, MikelFunction>;
|
|
31
33
|
};
|
|
32
34
|
|
|
33
|
-
export type
|
|
34
|
-
initialState?:
|
|
35
|
+
export type MikelPluginOptions = MikelOptions & {
|
|
36
|
+
initialState?: MikelState;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export type MikelContext = {
|
|
40
|
+
helpers: Record<string, MikelFunction>;
|
|
41
|
+
functions: Record<string, MikelFunction>;
|
|
42
|
+
partials: Record<string, MikelPartial>;
|
|
43
|
+
hooks: {
|
|
44
|
+
add: (hookName: string, listener: Function) => void;
|
|
45
|
+
call: (hookName: string, ...args: any[]) => void;
|
|
46
|
+
callWaterfall: (hookName: string, value: any) => any;
|
|
47
|
+
};
|
|
48
|
+
initialState: MikelState;
|
|
35
49
|
};
|
|
36
50
|
|
|
51
|
+
export type MikelPlugin = (ctx: MikelContext) => void;
|
|
52
|
+
|
|
37
53
|
export type Mikel = {
|
|
38
54
|
(template: string, data?: any): string;
|
|
39
|
-
use(
|
|
55
|
+
use(plugin: Partial<MikelPluginOptions> | MikelPlugin): void;
|
|
40
56
|
addHelper(name: string, fn: MikelHelper): void;
|
|
41
57
|
removeHelper(name: string): void;
|
|
42
58
|
addFunction(name: string, fn: MikelFunction): void;
|
|
43
59
|
removeFunction(name: string): void;
|
|
44
60
|
addPartial(name: string, partial: string | MikelPartial): void;
|
|
45
61
|
removePartial(name: string): void;
|
|
46
|
-
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export type MikelWrapperPlugin = (options: { header?: string, footer?: string }) => MikelPlugin;
|
|
65
|
+
export type MikelStatePlugin = (state: MikelState) => MikelPlugin;
|
|
47
66
|
|
|
48
67
|
declare const mikel: {
|
|
49
68
|
(template: string, data?: any, options?: Partial<MikelOptions>): string;
|
|
@@ -53,6 +72,8 @@ declare const mikel: {
|
|
|
53
72
|
parse(value: string, context?: any, vars?: any): any;
|
|
54
73
|
tokenize(str: string): string[];
|
|
55
74
|
untokenize(tokens: string[], start?: string, end?: string): string;
|
|
75
|
+
WrapperPlugin: MikelWrapperPlugin;
|
|
76
|
+
StatePlugin: MikelStatePlugin;
|
|
56
77
|
};
|
|
57
78
|
|
|
58
79
|
export default mikel;
|
package/index.js
CHANGED
|
@@ -122,6 +122,24 @@ const findClosingToken = (tokens, i, token) => {
|
|
|
122
122
|
throw new Error(`Unmatched section end: {{${token}}}`);
|
|
123
123
|
};
|
|
124
124
|
|
|
125
|
+
// @description create a hook manager for the provided hooks map
|
|
126
|
+
const createHookManager = (hooks = new Map()) => {
|
|
127
|
+
return {
|
|
128
|
+
add: (hookName, listener) => {
|
|
129
|
+
if (!hooks.has(hookName.toLowerCase())) {
|
|
130
|
+
hooks.set(hookName.toLowerCase(), []);
|
|
131
|
+
}
|
|
132
|
+
hooks.get(hookName.toLowerCase()).push(listener);
|
|
133
|
+
},
|
|
134
|
+
call: (hookName, ...args) => {
|
|
135
|
+
hooks.get(hookName.toLowerCase())?.forEach((listener) => listener(...args));
|
|
136
|
+
},
|
|
137
|
+
callWaterfall: (hookName, value) => {
|
|
138
|
+
return (hooks.get(hookName.toLowerCase()) || []).reduce((v, listener) => listener(v), value);
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
};
|
|
142
|
+
|
|
125
143
|
// @description internal method to compile the template
|
|
126
144
|
const compile = (ctx, tokens, output, data, state, index = 0, section = "") => {
|
|
127
145
|
let i = index;
|
|
@@ -278,18 +296,26 @@ const create = (options = {}) => {
|
|
|
278
296
|
partials: Object.assign({}, options?.partials || {}),
|
|
279
297
|
functions: Object.assign({}, options?.functions || {}),
|
|
280
298
|
initialState: {}, // Object.assign({}, options?.initialState || {}),
|
|
299
|
+
hooks: createHookManager(new Map()),
|
|
281
300
|
});
|
|
282
301
|
// entry method to compile the template with the provided data object
|
|
283
|
-
const compileTemplate = (
|
|
302
|
+
const compileTemplate = (originalTemplate, data = {}) => {
|
|
303
|
+
const output = [];
|
|
304
|
+
const template = ctx.hooks.callWaterfall("prerender", originalTemplate || "");
|
|
284
305
|
compile(ctx, tokenize(template), output, data, { ...ctx.initialState, root: data }, 0, "");
|
|
285
|
-
return output.join("");
|
|
306
|
+
return ctx.hooks.callWaterfall("postrender", output.join(""));
|
|
286
307
|
};
|
|
287
308
|
// assign api methods and return method to compile the template
|
|
288
309
|
return Object.assign(compileTemplate, {
|
|
289
|
-
use: (
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
}
|
|
310
|
+
use: (plugin) => {
|
|
311
|
+
if (typeof plugin === "function") {
|
|
312
|
+
plugin(ctx);
|
|
313
|
+
}
|
|
314
|
+
else if (typeof plugin === "object" && !!plugin) {
|
|
315
|
+
["helpers", "functions", "partials", "initialState"].forEach(field => {
|
|
316
|
+
Object.assign(ctx[field], plugin?.[field] || {});
|
|
317
|
+
});
|
|
318
|
+
}
|
|
293
319
|
},
|
|
294
320
|
addHelper: (name, fn) => ctx.helpers[name] = fn,
|
|
295
321
|
removeHelper: name => delete ctx.helpers[name],
|
|
@@ -305,6 +331,22 @@ const mikel = (template = "", data = {}, options = {}) => {
|
|
|
305
331
|
return create(options)(template, data);
|
|
306
332
|
};
|
|
307
333
|
|
|
334
|
+
// @description plugin to wrap template with custom text
|
|
335
|
+
mikel.WrapperPlugin = (options = {}) => {
|
|
336
|
+
return (context) => {
|
|
337
|
+
context.hooks.add("prerender", template => {
|
|
338
|
+
return [options.header || "", template, options.footer || ""].join("");
|
|
339
|
+
});
|
|
340
|
+
};
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
// @description plugin to define state variables
|
|
344
|
+
mikel.StatePlugin = (state = {}) => {
|
|
345
|
+
return (context) => {
|
|
346
|
+
Object.assign(context.initialState, state || {});
|
|
347
|
+
};
|
|
348
|
+
};
|
|
349
|
+
|
|
308
350
|
// @description assign utilities
|
|
309
351
|
mikel.create = create;
|
|
310
352
|
mikel.escape = escape;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mikel",
|
|
3
3
|
"description": "Micro templating library with zero dependencies",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.34.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Josemi Juanes",
|
|
@@ -19,7 +19,8 @@
|
|
|
19
19
|
},
|
|
20
20
|
"scripts": {
|
|
21
21
|
"release": "node ./scripts/release.js",
|
|
22
|
-
"test": "node test.js && yarn test:eval && yarn test:frontmatter && yarn test:markdown",
|
|
22
|
+
"test": "node test.js && yarn test:cli && yarn test:eval && yarn test:frontmatter && yarn test:markdown",
|
|
23
|
+
"test:cli": "cd packages/mikel-cli && yarn test",
|
|
23
24
|
"test:eval": "node ./packages/mikel-eval/test.js",
|
|
24
25
|
"test:frontmatter": "node ./packages/mikel-frontmatter/test.js",
|
|
25
26
|
"test:markdown": "node ./packages/mikel-markdown/test.js"
|