UNPKG

11.4 kBMarkdownView Raw
1[![npm][npm]][npm-url]
2[![node][node]][node-url]
3[![deps][deps]][deps-url]
4[![tests][tests]][tests-url]
5[![coverage][cover]][cover-url]
6[![code style][style]][style-url]
7[![chat][chat]][chat-url]
8
9<div align="center">
10 <img width="110" height="100" title="PostHTML Plugin" vspace="50" src="http://michael-ciniawsky.github.io/postcss-load-plugins/logo.svg">
11 <img width="220" height="200" title="PostHTML" src="http://posthtml.github.io/posthtml/logo.svg">
12 <h1>Expressions Plugin</h1>
13</div>
14
15<h2 align="center">Install</h2>
16
17```bash
18npm i -D posthtml-expressions
19```
20
21<h2 align="center">Usage</h2>
22
23```js
24const { readFileSync } = require('fs')
25
26const posthtml = require('posthtml')
27const expressions = require('posthtml-expressions')
28
29posthtml(expressions({ locals: { foo: 'bar' } }))
30 .process(readFileSync('index.html', 'utf8'))
31 .then((result) => console.log(result.html))
32```
33
34This plugin provides a syntax for including local variables and expressions in your templates, and also extends custom tags to act as helpers for conditionals and looping.
35
36You have full control over the delimiters used for injecting locals, as well as the tag names for the conditional and loop helpers, if you need them. All options that can be passed to the `expressions` plugin are shown below:
37
38<h2 align="center">Options</h2>
39
40|Option|Default|Description|
41|:----:|:-----:|:----------|
42| **delimiters** | `['{{', '}}']` | Array containing beginning and ending delimiters for escaped locals |
43| **unescapeDelimiters** | `['{{{', '}}}']` | Array containing beginning and ending delimiters for unescaped locals |
44| **locals** | `{}` | Object containing any local variables you want to be available inside your expressions |
45| **conditionalTags** | `['if', 'elseif', 'else']` | Array containing names for tags used for `if/else if/else` statements |
46| **switchTags** | `['switch', 'case', 'default']` | Array containing names for tags used for `switch/case/default` statements |
47| **loopTags** | `['each']` | Array containing names for `for` loops |
48| **scopeTags** | `['scope']` | Array containing names for scopes |
49| **ignoredTag** | `'raw'` | String containing name of tag inside which parsing is disabled |
50
51### Locals
52
53You can inject locals into any piece of content in your html templates, other than overwriting tag names. For example, if you passed the following config to the expressions plugin:
54
55```js
56locals: { className: 'intro', name: 'Marlo' }
57```
58
59```html
60<div class="{{ className }}">
61 My name is {{ name }}
62</div>
63```
64
65```html
66<div class="intro">
67 My name is Marlo
68</div>
69```
70
71### Unescaped Locals
72
73By default, special characters will be escaped so that they show up as text, rather than html code. For example, if you had a local containing valid html as such:
74
75```js
76locals: { statement: '<strong>wow!</strong>' }
77```
78
79```html
80<p>The fox said, {{ statement }}</p>
81```
82
83```html
84<p>The fox said, &lt;strong&gt;wow!&lt;strong&gt;</p>
85```
86
87In your browser, you would see the angle brackets, and it would appear as intended. However, if you wanted it instead to be parsed as html, you would need to use the `unescapeDelimiters`, which by default are three curly brackets, like this:
88
89```html
90<p>The fox said, {{{ strongStatement }}}</p>
91```
92
93In this case, your code would render as html:
94
95```html
96<p>The fox said, <strong>wow!<strong></p>
97```
98
99### Expressions
100
101You are not limited to just directly rendering local variables either, you can include any type of javascript expressions and it will be evaluated, with the result rendered. For example:
102
103```html
104<p class="{{ env === 'production' ? 'active' : 'hidden' }}">in production!</p>
105```
106
107With this in mind, it is strongly recommended to limit the number and complexity of expressions that are run directly in your template. You can always move the logic back to your config file and provide a function to the locals object for a smoother and easier result. For example:
108
109```js
110locals: {
111 isProduction: (env) => env === 'production' ? 'active' : 'hidden'
112}
113```
114
115```html
116<p class="{{ isProduction(env) }}">in production!</p>
117```
118
119#### Ignoring Expressions
120
121Many JavaScript frameworks use `{{` and `}}` as expression delimiters. It can even happen that another framework uses the same _custom_ delimiters you have defined in this plugin.
122
123You can tell the plugin to completely ignore an expression by prepending `@` to the delimiters:
124
125```html
126<p>The @{{ foo }} is strong with this one.</p>
127```
128
129Result:
130
131```html
132<p>The {{ foo }} is strong with this one.</p>
133```
134
135### Conditionals
136
137Conditional logic uses normal html tags, and modifies/replaces them with the results of the logic. If there is any chance of a conflict with other custom tag names, you are welcome to change the tag names this plugin looks for in the options. For example, given the following config:
138
139```js
140locals: { foo: 'foo' }
141```
142
143```html
144<if condition="foo === 'bar'">
145 <p>Foo really is bar! Revolutionary!</p>
146</if>
147
148<elseif condition="foo === 'wow'">
149 <p>Foo is wow, oh man.</p>
150</elseif>
151
152<else>
153 <p>Foo is probably just foo in the end.</p>
154</else>
155```
156
157```html
158<p>Foo is probably just foo in the end.</p>
159```
160
161Anything in the `condition` attribute is evaluated directly as an expressions.
162
163It should be noted that this is slightly cleaner-looking if you are using the [SugarML parser](https://github.com/posthtml/sugarml). But then again so is every other part of html.
164
165```sml
166if(condition="foo === 'bar'")
167 p Foo really is bar! Revolutionary!
168
169elseif(condition="foo === 'wow'")
170 p Foo is wow, oh man.
171
172else
173 p Foo is probably just foo in the end.
174```
175
176### Switch statement
177
178Switch statements act like streamline conditionals. They are useful for when you want to compare a single variable against a series of constants.
179
180```js
181locals: { foo: 'foo' }
182```
183
184```html
185<switch expression="foo">
186 <case n="'bar'">
187 <p>Foo really is bar! Revolutionary!</p>
188 </case>
189 <case n="'wow'">
190 <p>Foo is wow, oh man.</p>
191 </case>
192 <default>
193 <p>Foo is probably just foo in the end.</p>
194 </default>
195</switch>
196```
197
198```html
199<p>Foo is probably just foo in the end.</p>
200```
201
202Anything in the `expression` attribute is evaluated directly as an expressions.
203
204### Loops
205
206You can use the `each` tag to build loops. It works with both arrays and objects. For example:
207
208```js
209locals: {
210 array: ['foo', 'bar'],
211 object: { foo: 'bar' }
212}
213```
214
215**Array**
216```html
217<each loop="item, index in array">
218 <p>{{ index }}: {{ item }}</p>
219</each>
220```
221
222```html
223<p>1: foo</p>
224<p>2: bar</p>
225```
226
227**Object**
228```html
229<each loop="value, key in anObject">
230 <p>{{ key }}: {{ value }}</p>
231</each>
232```
233
234```html
235<p>foo: bar</p>
236```
237
238The value of the `loop` attribute is not a pure expressions evaluation, and it does have a tiny and simple custom parser. Essentially, it starts with one or more variable declarations, comma-separated, followed by the word `in`, followed by an expressions.
239
240
241```html
242<each loop="item in [1,2,3]">
243 <p>{{ item }}</p>
244</each>
245```
246
247So you don't need to declare all the available variables (in this case, the index is skipped), and the expressions after `in` doesn't need to be a local variable, it can be any expressions.
248
249#### Loop meta
250
251Inside a loop, you have access to a special `loop` object, which contains information about the loop currently being executed:
252
253- `loop.index` - the current iteration of the loop (0 indexed)
254- `loop.remaining` - number of iterations until the end (0 indexed)
255- `loop.first` - boolean indicating if it's the first iteration
256- `loop.last` - boolean indicating if it's the last iteration
257- `loop.length` - total number of items
258
259Example:
260
261```html
262<each loop='item in [1,2,3]'>
263 <li>Item value: {{ item }}</li>
264 <li>Current iteration of the loop: {{ loop.index }}</li>
265 <li>Number of iterations until the end: {{ loop.remaining }} </li>
266 <li>This {{ loop.first ? 'is' : 'is not' }} the first iteration</li>
267 <li>This {{ loop.last ? 'is' : 'is not' }} the last iteration</li>
268 <li>Total number of items: {{ loop.length }}</li>
269</each>
270```
271
272### Scopes
273
274You can replace locals inside certain area wrapped in a `<scope>` tag. For example you can use it after [posthtml-include](https://github.com/posthtml/posthtml-include)
275
276```js
277locals: {
278 author: { name: 'John'},
279 editor: { name: 'Jeff'}
280}
281```
282
283```html
284<scope with="author">
285 <include src="components/profile.html"></include>
286</scope>
287<scope with="editor">
288 <include src="components/profile.html"></include>
289</scope>
290```
291
292```html
293<div class="profile">
294 <div class="profile__name">{{ name }}</div>
295 <img class="profile__avatar" src="{{ image }}" alt="{{ name }}'s avatar" />
296 <a class="profile__link" href="{{ link }}">more info</a>
297</div>
298```
299
300### Ignored tag
301
302Anything inside this tag will not be parsed, allowing you to output delimiters and anything the plugin would normally parse, in their original form.
303
304```html
305<raw>
306 <if condition="foo === 'bar'">
307 <p>Output {{ foo }} as-is</p>
308 </if>
309</raw>
310```
311
312```html
313<if condition="foo === 'bar'">
314 <p>Output {{ foo }} as-is</p>
315</if>
316```
317
318You can customize the name of the tag:
319
320```js
321var opts = {
322 ignoredTag: 'verbatim',
323 locals: { foo: 'bar' } }
324}
325
326posthtml(expressions(opts))
327 .process(readFileSync('index.html', 'utf8'))
328 .then((result) => console.log(result.html))
329```
330
331```html
332<verbatim>
333 <if condition="foo === 'bar'">
334 <p>Output {{ foo }} as-is</p>
335 </if>
336</verbatim>
337```
338
339```html
340<if condition="foo === 'bar'">
341 <p>Output {{ foo }} as-is</p>
342</if>
343```
344
345<h2 align="center">Maintainers</h2>
346
347<table>
348 <tbody>
349 <tr>
350 <td align="center">
351 <img width="150 height="150"
352 src="https://avatars.githubusercontent.com/u/556932?v=3&s=150">
353 <br>
354 <a href="https://github.com/jescalan">Jeff Escalante</a>
355 </td>
356 <td align="center">
357 <img width="150 height="150"
358 src="https://avatars.githubusercontent.com/u/7034281?v=3&s=150">
359 <br>
360 <a href="https://github.com/mrmlnc">Denis Malinochkin</a>
361 </td>
362 </tr>
363 <tbody>
364</table>
365
366<h2 align="center">Contributors</h2>
367
368<table>
369 <tbody>
370 <tr>
371 <td align="center">
372 <img width="150 height="150"
373 src="https://avatars.githubusercontent.com/u/5419992?v=3&s=150">
374 <br>
375 <a href="https://github.com/michael-ciniawsky">Michael Ciniawsky</a>
376 </td>
377 <td align="center">
378 <img width="150 height="150"
379 src="https://avatars.githubusercontent.com/u/17473315?v=3&s=150">
380 <br>
381 <a href="https://github.com/xakdog">Krillin</a>
382 </td>
383 </tr>
384 <tbody>
385</table>
386
387
388[npm]: https://img.shields.io/npm/v/posthtml-expressions.svg
389[npm-url]: https://npmjs.com/package/posthtml-expressions
390
391[node]: https://img.shields.io/node/v/posthtml-expressions.svg
392[node-url]: https://nodejs.org/
393
394[deps]: https://david-dm.org/posthtml/posthtml-expressions.svg
395[deps-url]: https://david-dm.org/posthtml/posthtml-expressions
396
397[tests]: http://img.shields.io/travis/posthtml/posthtml-expressions.svg
398[tests-url]: https://travis-ci.org/posthtml/posthtml-expressions
399
400[cover]: https://coveralls.io/repos/github/posthtml/posthtml-expressions/badge.svg
401[cover-url]: https://coveralls.io/github/posthtml/posthtml-expressions
402
403[style]: https://img.shields.io/badge/code%20style-standard-yellow.svg
404[style-url]: http://standardjs.com/
405
406[chat]: https://badges.gitter.im/posthtml/posthtml.svg
407[chat-url]: https://gitter.im/posthtml/posthtml?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge"