Comparing version 4.0.1 to 4.0.2
# CHANGELOG | ||
### 4.0.1 | ||
* A lot of refactoring in compiler and runtime. | ||
* Improved performance in 1.5 times. | ||
* New feature: extendable templates. | ||
* New feature: contexts. | ||
* Added support to import anything. | ||
* Added this expression support. | ||
* Fixed scope variable updates. | ||
* Dropped support for wrappers. | ||
* Dropped support for parsers. | ||
* Dropped block statements. | ||
### 3.8.1 | ||
@@ -106,2 +118,2 @@ | ||
* First working prototype. | ||
{ | ||
"name": "monkberry", | ||
"version": "4.0.1", | ||
"version": "4.0.2", | ||
"description": "Monkberry is a JavaScript library for building web user interfaces", | ||
@@ -5,0 +5,0 @@ "bin": "bin/monkberry", |
554
README.md
@@ -1,537 +0,103 @@ | ||
# Monkberry - JavaScript template engine | ||
# Monkberry | ||
[![npm](https://img.shields.io/npm/v/monkberry.svg)](https://www.npmjs.com/package/monkberry) | ||
[![Build Status](https://travis-ci.org/monkberry/monkberry.svg?branch=master)](https://travis-ci.org/monkberry/monkberry) | ||
Monkberry compile template to JavaScript code for creating nodes with DOM API and helper methods for updating content of these nodes. | ||
Monkberry is **blazingly fast**, **small `1kb`** and **simple** JavaScript library for building **web user interfaces**. | ||
``` | ||
npm install monkberry --save | ||
``` | ||
## Features | ||
* Small, dependency free | ||
* Simple and minimalistic | ||
* Fully tested | ||
* Precompiled templates | ||
* SourceMaps | ||
* Custom tags | ||
* Extremely fast! | ||
## Table of Contents | ||
- [Example](#example) | ||
- [Documentation](#documentation) | ||
- [Getting Started](#getting-started) | ||
- [Expressions](#expressions) | ||
- [If, Else](#if-else) | ||
- [For](#for) | ||
- [Default values](#default-values) | ||
- [Filters](#filters) | ||
- [Custom tags](#custom-tags) | ||
- [Spread attributes](#spread-attributes) | ||
- [Importing](#importing) | ||
- [Event Handling](#event-handling) | ||
- [Globals](#globals) | ||
- [Prerender](#prerender) | ||
- [Transforms](#transforms) | ||
- [Unsafe](#unsafe) | ||
- [Comments](#comments) | ||
- [Blocks](#blocks) | ||
- [API Reference](#api-reference) | ||
- [Monkberry](#monkberry) | ||
- [monkberry.render(template, node[, options])](#monkberryrendername-values-nocache) | ||
- [monkberry.prerender(template, times)](#monkberryprerendername-times) | ||
- [Monkberry.prototype.appendTo(toNode)](#viewappendtotonode) | ||
- [Monkberry.prototype.insertBefore(toNode)](#viewinsertbeforetonode) | ||
- [Monkberry.prototype.createDocument()](#viewcreatedocument) | ||
- [Monkberry.prototype.update(data)](#viewupdatedata) | ||
- [Monkberry.prototype.remove()](#viewremoveforce) | ||
- [Monkberry.prototype.querySelector(query)](#viewqueryselectorquery) | ||
- [Tests](#tests) | ||
- [Plugins](#plugins) | ||
- [Benchmarks](#benchmarks) | ||
## Example | ||
Monkberry will compile this template: | ||
```html | ||
<div> | ||
<h1>{{ title }}</h1> | ||
<p> | ||
{{ text }} | ||
</p> | ||
</div> | ||
``` | ||
Monkberry comes with powerfull templating engine, which is compiled to JavaScript. | ||
To JavaScript code like this: | ||
```js | ||
var div = document.createElement('div'); | ||
var h1 = document.createElement('h1'); | ||
var p = document.createElement('p'); | ||
div.appendChild(h1); | ||
div.appendChild(p); | ||
... | ||
view.update = function (data) { | ||
h1.textContent = data.title; | ||
p.textContent = data.text; | ||
}; | ||
``` | ||
Which can be used like that: | ||
```js | ||
var view = Monkberry.render(template, document.body); | ||
view.update({ | ||
title: 'Monkberry', | ||
text: 'JavaScript DOM template engine' | ||
}); | ||
``` | ||
## Documentation | ||
### Getting Started | ||
Monkberry has support for both browserify via [monkberrify](https://github.com/monkberry/monkberrify) and for webpack via [monkberry-loader](https://github.com/monkberry/monkberry-loader). | ||
Monkberry can be used like CLI tool. Install Monkberry globally: | ||
``` | ||
npm install monkberry -g | ||
``` | ||
To compile all templates into single JavaScript file with source map run next command: | ||
``` | ||
monkberry --source-map --output template.js templates/*.html | ||
``` | ||
Require generated `template.js` and `monkberry.js` files and render that view. | ||
```js | ||
var Monkberry = require('monkberry'); | ||
var Template = require('./template.js'); | ||
var view = Monkberry.render(Template, document.body); | ||
``` | ||
Now, to update data of view on page: | ||
```js | ||
view.update({...}); | ||
// or update only what's needed | ||
view.update({key: value}); | ||
``` | ||
### Expressions | ||
Monkberry perceives everything inside `{{` and `}}` mustache as JavaScript expression. | ||
```html | ||
<div class="greetings {{ visible ? '' : 'hidden' }}"> | ||
Hello, {{ user.name + "!" }} | ||
</div> | ||
``` | ||
### If, Else | ||
Can be any valid JavaScrpt expressions. | ||
```twig | ||
{% if count < 0 || count > 10 %} | ||
... | ||
{% else %} | ||
... | ||
{% endif %} | ||
<ol> | ||
{% for todos %} | ||
<li> | ||
{% if complete %} | ||
<del>{{ text }}</del> | ||
{% else %} | ||
<em>{{ text }}</em> | ||
{% endif %} | ||
</li> | ||
{% endfor %} | ||
</ol> | ||
``` | ||
Any number on variables in `if`: | ||
```twig | ||
{% if array.indexOf(search) != -1 %} | ||
... | ||
{% endif %} | ||
``` | ||
> Note what Monkberry update only one of `if`/`else` block. | ||
> ```twig | ||
> {% if check %} | ||
> Then {{ value }}! | ||
> {% else %} | ||
> Else {{ value }}! | ||
> {% endif %} | ||
> ``` | ||
> Render that template: | ||
> ```js | ||
> var view = Monkberry.render(Example, document.body); | ||
> view.update({ | ||
> check: true, | ||
> value: 'one' | ||
> }); | ||
> ``` | ||
> View will be `Then one!`. When if update view: | ||
> ```js | ||
> view.update({ | ||
> check: false, | ||
> value: 'two' | ||
> }); | ||
> ``` | ||
> View will be `Else two!`. But if update only `check`, variable of then part will be same as before. | ||
> ```js | ||
> view.update({check: true}); | ||
> ``` | ||
> View will be `Then one!`. | ||
> | ||
> This is happens because Monkberry does not stores variables passed to `update` function, it stores only DOM nodes. | ||
> Monkberry will update only one part of `if`/`else`. | ||
and then | ||
### For | ||
Monkberry can loop other arrays and objects as well. | ||
```twig | ||
{% for array %} | ||
{{ name }} | ||
{% endfor %} | ||
``` | ||
In this form, body of `for` has access only for variables iterating on. | ||
```js | ||
view.update({ | ||
array: [ | ||
{name: 'Anton'}, | ||
... | ||
] | ||
}); | ||
``` | ||
import Monkberry from 'monkberry'; | ||
import Template from 'template.monk'; | ||
To access outer scope specify iterator name. | ||
const view = Monkberry.render(Template, document.body); | ||
```twig | ||
{% for user of array %} | ||
{{ user.name }} | ||
{% endfor %} | ||
view.update({todos: [...]}); | ||
``` | ||
Also key can be specified. | ||
```twig | ||
{% for key, user of array %} | ||
{{ key }}: {{ user.name }} | ||
{% endfor %} | ||
``` | ||
## Features | ||
### Default values | ||
* Small **`1kb`** minified & gzipped | ||
* Simple, small learning curve | ||
* Fully tested | ||
* Precompiled templates | ||
* Source maps | ||
* Custom tags | ||
* Blazingly fast (only necessary dom updates) | ||
```twig | ||
<div class="foo {{ modify || 'baz' }}"> | ||
{{ content || "No content" }} | ||
</div> | ||
``` | ||
## Documentation | ||
View rendered without data will be filled with default data: | ||
Documentation available on [monkberry.js.org](http://monkberry.js.org) site. | ||
```twig | ||
<div class="foo baz"> | ||
No content | ||
</div> | ||
``` | ||
## Development | ||
Note if you will use some variable in right side of _OR_ operator, what can't be used as default data. | ||
```twig | ||
{{ content || "No content" + foo }} | ||
``` | ||
If you want to hack on Monkberry, the first step is to fork the repo. | ||
```sh | ||
# Build compiler | ||
npm run build | ||
# Build parser | ||
npm run build:parser | ||
### Filters | ||
# Watch changes and rebuild | ||
npm run watch | ||
Any expression support filter statement. | ||
```twig | ||
Hello, {{ user.name | upper }} | ||
# Start tests server | ||
testem | ||
``` | ||
To define that filter: | ||
```js | ||
Template.filters.upper = function (text) { | ||
return text.toUpperCase(); | ||
}; | ||
``` | ||
## Plugins | ||
Also Monkberry understand parameters for filters: | ||
```js | ||
template.filters.replace = function (text, from, to) { | ||
return text.replace(from, to); | ||
}; | ||
``` | ||
* [Atom Package](https://atom.io/packages/language-monkberry) | ||
* [Sublime Text Package](https://github.com/monkberry/language-monkberry) | ||
```twig | ||
{{ text | replace(/.../, '$1') }} | ||
``` | ||
## Performance | ||
And allow to combine filters: | ||
```twig | ||
{{ text | lower | replace(/.../, '$1') | upper }} | ||
``` | ||
#### [Benchmarks](http://monkberry.github.io/benchmark/) | ||
That expression will be compiled to next JavaScript: | ||
```js | ||
upper(replace(lower(text), /.../, '$1')); | ||
``` | ||
Why is Monkberry so fast? Even in comparison with React, Monkberry is **10 times faster**, sometimes **100 times faster**. | ||
It's because Monkberry will do only necessary dom updates, and does it in a completely different way than React does. | ||
Monkberry compiles template to plain JavaScript to gain an advantage by using v8 **hidden classes** and **reduce call stack**. | ||
There is no virtual dom (in general, an react app have to keep 3 virtual doms), for example next template will be generated to JavaScript code which will do only necessary dom updates on state changes. | ||
Filters can be used in expressions, `if` and `for` statements. | ||
### Custom tags | ||
Custom tag template `greet.monk`: | ||
```twig | ||
<div> | ||
{{ value }}, {{ name }}! | ||
... | ||
<h1>{{ title }}</h1> | ||
... | ||
</div> | ||
``` | ||
To render that custom tag in another template: | ||
Will be compiled to code like this: | ||
```twig | ||
{% import greet from './greet.monk' %} | ||
<greet value="Hello" name="world"> | ||
<greet value="Hello" name="{{ user.name }}"> | ||
``` | ||
### Spread attributes | ||
Spread attributes allow easily convert object into node attributes. | ||
The properties of the object that you pass in are copied onto the node's attributes. | ||
```twig | ||
<input {{...attr}}/> | ||
``` | ||
```js | ||
view.update({attr: { | ||
id: 'foo', | ||
value: 'baz' | ||
}}); | ||
function (state) { | ||
h1.textContent = state.title; | ||
} | ||
``` | ||
You can combine it with other attributes. | ||
Benchmarks covers a few use cases, and compare Monkberry with React and innerHTML. Also it's contains real site code and data. | ||
```twig | ||
<input {{...attr}} value={{ value }}/> | ||
``` | ||
Note what later updates of attributes override previous ones. | ||
## License | ||
```js | ||
view.update({value: 'baz'}); | ||
// ... | ||
view.update({attr: {value: 'new baz'}}); // Will override previous value. | ||
``` | ||
Spread operator also works well with custom attributes. In fact, this is best way to pass data into custom tag. | ||
```twig | ||
<my-tag {{...attr}}/> | ||
``` | ||
```twig | ||
<my-tag> | ||
<input type={{ type }} value={{ value }}> | ||
</my-tag> | ||
``` | ||
### Importing | ||
It is possible to require template within another template. | ||
```twig | ||
{% import Component './Component.monk' %} | ||
<Component/> | ||
``` | ||
Also it's possible to include any JS file or module: | ||
```twig | ||
{% import upperCase 'upper-case' %} | ||
// ... | ||
{{ upperCase(name) }} | ||
``` | ||
### Event Handling | ||
There are a few ways to deal with event handling in Monkberry. | ||
Add event listener to node directly: | ||
```js | ||
view.querySelector('.button').addEventListener('click', function (event) { | ||
... | ||
}); | ||
``` | ||
But this is difficult when dealing with conditions and loops. | ||
Better approach is to use [event delegating](https://github.com/monkberry/events). | ||
```js | ||
view.on('click', '.button', function (event) { | ||
... | ||
}); | ||
``` | ||
### Globals | ||
Monkberry also support global variables. This is very usefull if using `window` variable inside of templates. | ||
Or if using translation function like this: `{{ __('greeting') + userName }}`. | ||
To do it, you need to specify globals as array of variables names for compiler to pass. Read monkberry loaders docs for more info. | ||
### Prerender | ||
To speedup render Monkberry can prerender DOM nodes to use them in future. | ||
```js | ||
Monkberry.prerender(Template, 10); // Preprender template 10 times. | ||
``` | ||
Then next `render` call will use one of these prerendered views: | ||
```js | ||
Monkberry.render(Template, node); // Will use already created DOM nodes. | ||
``` | ||
This is very usefull to do then browser waiting some xhr request. | ||
### Transforms | ||
Transformers allow to modify [AST](https://en.wikipedia.org/wiki/Abstract_syntax_tree) before compilation of templates. | ||
List of AST nodes can be founded here: [ast.js](https://github.com/monkberry/parser/blob/master/src/ast.js) | ||
Example of transform which trim whitespaces: [whitespace.js](https://github.com/monkberry/monkberry/blob/master/src/optimize/whitespace.js) | ||
Add transforms to Monkbeery before compilation: | ||
```js | ||
import { Compiler } from 'monkberry'; | ||
import { myTransform } from './myTransform'; | ||
var compiler = new Compiler(); | ||
compiler.transforms.custom = myTransform; | ||
``` | ||
### Unsafe | ||
Monkberry escape all inserted variables by default. But if some times you want to insert | ||
some HTML template via variable you can you _unsafe_ statement which is using `innerHTML`. | ||
Improper use of the _unsafe_ statement can open you up to a [cross-site scripting (XSS)](https://en.wikipedia.org/wiki/Cross-site_scripting) attack. | ||
```twig | ||
{% unsafe '<a href="XSS">...</a>' %} | ||
{% unsafe html %} | ||
``` | ||
### Comments | ||
You can use standard html comments. | ||
```twig | ||
<!-- Comment does here --> | ||
``` | ||
Comments will be cut out from template. | ||
## API Reference | ||
Monkberry API strictly follows [semantic versioning](http://semver.org). | ||
### Monkberry | ||
#### Monkberry.render(template, node, options) | ||
Render template, and returns new `Monkberry` instance. | ||
#### Monkberry.prerender(template, times) | ||
Generates views for future calls of render method. | ||
#### Monkberry.prototype.appendTo(toNode) | ||
Append rendered view nodes to specified node. | ||
* `toNode`: `Element` - DOM node. | ||
#### Monkberry.prototype.insertBefore(toNode) | ||
Insert rendered view nodes before specified node. | ||
* `toNode`: `Element` - DOM node. | ||
#### Monkberry.prototype.createDocument() | ||
Return view's nodes. Note what if your template contains more then one root element, `createDocument` function will | ||
return `DocumentFragment` what contains all these nodes. If you have only one root node, it will be returned as is. | ||
#### Monkberry.prototype.update(data) | ||
Update rendered template with new data. You can specify only part of data to update or update entire data. | ||
* `data`: `Object|Array` - values to update in template. | ||
Example: | ||
```js | ||
var data = { | ||
title: 'Title #1', | ||
content: '...' | ||
}; | ||
view.update({title: 'Title #2'}); | ||
``` | ||
#### Monkberry.prototype.remove([) | ||
Remove view's nodes from document, and puts it to pool for future reuse. | ||
#### view.querySelector(query) | ||
Select node by query. | ||
* `query`: `string` - query to select node. | ||
> Note what this function uses [Element.matches()](https://developer.mozilla.org/en-US/docs/Web/API/Element/matches) for checking root nodes. Include polyfill for matches if you use it. | ||
If you template contains more then one nodes on first level, `querySelector` will look other all subtrees. Array of all top level nodes can be accessed by `view.nodes[]` array. | ||
> Note what querySelector can not work with template which have if/for/custom node on first level. | ||
> ```twig | ||
> {% if cond %} | ||
> ... | ||
> {% endif %} | ||
> ``` | ||
> You will got exception like this: `Can not use querySelector with non-element nodes on first level.` | ||
> | ||
> Solution is to wrap such statement into another node: | ||
> ```twig | ||
> <div> | ||
> {% if cond %} | ||
> ... | ||
> {% endif %} | ||
> </div> | ||
> ``` | ||
## Tests | ||
Monkberry uses [Jasmine](http://jasmine.github.io) and [testem](https://github.com/airportyh/testem). To run test locally run: | ||
``` | ||
testem ci | ||
``` | ||
## Plugins | ||
* [Atom Package](https://atom.io/packages/language-monkberry) | ||
* [Sublime Text Package](https://github.com/monkberry/language-monkberry) | ||
## Benchmarks | ||
Benchmarks covers a few use cases, and compare Monkberry with [React](https://facebook.github.io/react/) and innerHTML. | ||
Also it's contains real site code for tests. | ||
* [monkberry.js.org/benchmark](http://monkberry.github.io/benchmark/) | ||
The MIT License (MIT) Copyright © 2016 Medvedev Anton |
@@ -7,2 +7,8 @@ # TODO | ||
* [ ] Default value for custom tag attributes [custom.js#L39](src/compiler/custom.js#L39) | ||
* [ ] ES2016 arrow function expressions `=>` | ||
* [ ] ES2016 arrow function expressions `=>` | ||
* [ ] Hot module replacment for monkberry-loader (good starting point is this commit [eb226f](https://github.com/monkberry/monkberry-loader/commit/eb226f23bc425617898d14d5d60ea3823d1bddec)) :star: | ||
* [ ] Server side rendering :star: | ||
* [ ] `{% elseif %}` support. | ||
* [ ] Template inheritance with `{% extends %}` :star: | ||
:star: — _complex, but interesting tasks_ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
211618
104