Merge branch 'master' into marko-v4

This commit is contained in:
Patrick Steele-Idem 2016-10-14 13:24:21 -06:00
commit b562a2e497
26 changed files with 250 additions and 13 deletions

View File

@ -3,6 +3,40 @@ Changelog
# 3.x
## 3.11.x
### 3.11.4
- Update to `async-writer@2`
### 3.11.3
- Silently ignore errors when parsing tag definition code inlined in JavaScript
### 3.11.2
- Fixed [#318](https://github.com/marko-js/marko/issues/318) - Use compiler options passed to require hook to configure marko globally
```javascript
require('marko/node-require').install({
compilerOptions: {
writeToDisk: false
}
});
```
### 3.11.1
- Fixed [#370](https://github.com/marko-js/marko/issues/370) - HTML characters in loop separator string should not be escaped
### 3.11.0
- Introduced the `<include-html(path)>` tag for including static HTML:
```xml
<include-html('./foo.html')>
```
## 3.10.x
### 3.10.1

View File

@ -103,6 +103,23 @@ html lang="en"
- No colors!
```
Alternatively, you can choose to apply rendering logic as separate "tags":
```html
<!DOCTYPE html>
html lang="en"
head
title - Marko Templating Engine
body
h1 - Hello ${data.name}!
if(data.colors.length)
ul
for(color in data.colors)
li - ${color}
else
div - No colors!
```
## Mixed syntax
You can even mix and match the concise syntax with the HTML syntax within the same document.

View File

@ -53,7 +53,7 @@ class ForEach extends Node {
body = body.items.concat([
builder.ifStatement(isNotLastTest, [
builder.text(separator)
builder.text(separator, false)
])
]);
}

View File

@ -1,12 +1,12 @@
/*
* Copyright 2011 eBay Software Foundation
*
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -24,7 +24,7 @@ var tokensRegExp = /"(?:[^"]|\\")*"|'(?:[^'])|(\/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n
function extractTagDef(code) {
var startMatches = tagStartRegExp.exec(code);
var tagDefStart;
var tagDefEnd;
@ -52,12 +52,24 @@ function extractTagDef(code) {
if (tagDefStart != null && tagDefEnd != null) {
var jsTagDef = code.substring(tagDefStart, tagDefEnd);
var tagDefObject = eval('(' + jsTagDef + ')');
var tagDefObject;
try {
// Try parsing it as JSON
tagDefObject = JSON.parse(jsTagDef);
} catch(e) {
// Try parsing it as JavaScript
try {
tagDefObject = eval('(' + jsTagDef + ')');
} catch(e) {
tagDefObject = {};
}
}
return tagDefObject;
}
} else {
return null;
}
}
}
exports.extractTagDef = extractTagDef;

View File

@ -100,7 +100,7 @@ Specifying this will prevent the rendering from aborting.
* __`name`__ (string): Name to assign to this await instance. Used for debugging purposes as well as by the `show-after` attribute (see below).
* __`placeholder`__ (string): Placeholder text to show while waiting for a data provider to complete. Only applicable if `client-reorder` is set to `true`.
* __`show-after`__ (string): When `client-reorder` is set to `true` then displaying this instance's content will be delayed until the referenced await instance is shown.
* __`timeout`__ (integer): Override the default timeout of 10 seconds with this param. Units are inmilliseconds so `timeout="40000"` would give a 40 second timeout.
* __`timeout`__ (integer): Override the default timeout of 10 seconds with this param. Units are inmilliseconds so `timeout=40000` would give a 40 second timeout.
* __`timeout-message`__ (string): Message to output if the data provider times out. Specifying this will prevent the rendering from aborting.
## `<await-placeholder>`

View File

@ -9,7 +9,7 @@ Yes, Marko has been battle-tested at [eBay](http://www.ebay.com/) and other comp
# Can templates be compiled on the client?
Possibly, but it is not recommended and it will likely not work in older browsers. The compiler is optimized to produce small, high performance compiled templates, but the compiler itself is not small and it comes bundled with some heavyweight modules such as a [JavaScript HTML parser](https://github.com/fb55/htmlparser2). In short, always compile your templates on the server. [Lasso.js](https://github.com/lasso-js/lasso) is recommended for including compiled templates as part of a web page.
Possibly, but it is not recommended and it will likely not work in older browsers. The compiler is optimized to produce small, high performance compiled templates, but the compiler itself is not small and it comes bundled with some heavyweight modules such as a [JavaScript HTML parser](https://github.com/philidem/htmljs-parser). In short, always compile your templates on the server. [Lasso.js](https://github.com/lasso-js/lasso) is recommended for including compiled templates as part of a web page.
# Which web browsers are supported?

View File

@ -181,6 +181,22 @@ var myIncludeTarget = require('./my-include-target.marko');
<include(data.myIncludeTarget) name="Frank" count=30/>
```
## Including static text
```xml
<include-text('./foo.txt')/>
```
NOTE: Special HTML characters will be escaped. If you do not want escaping then use the `<include-html>` tag (see below)
## Including static HTML
```xml
<include-html('./foo.html')/>
```
NOTE: Special HTML characters will _not_ be escaped since the file is expected to be an HTML file.
# Variables
Input data passed to a template is made available using a special `data` variable. It's possible to declare your own variables as shown in the following sample code:
@ -616,6 +632,43 @@ Output:
2c1b0a
```
## while
Any element can be repeated until a condition is met by using the `while` directive. The directive can be applied as an element or as an attribute.
_Applied as an attribute:_
```xml
<var n=0/>
<ul>
<li while(n < 4)>
${n++}
</li>
</ul>
```
_Applied as an element:_
```xml
<var n=0/>
<ul>
<while(n < 4)>
<li>${n++}</li>
</while>
</ul>
```
In both cases the output would be the following:
```html
<ul>
<li>0</li>
<li>1</li>
<li>2</li>
</ul>
```
# Macros
Parameterized macros allow for reusable fragments within an HTML template. A macro can be defined using the `<def>` directive.

View File

@ -167,7 +167,7 @@ app.get('/profile', function(req, res) {
Most front-end developers are familiar with, and comfortable with, templating languages such as [Handlebars](https://github.com/wycats/handlebars.js), [Dust](https://github.com/linkedin/dustjs) or [Mustache](http://mustache.github.io/) so why was Marko introduced?
What makes Marko different is that it is an HTML-based templating language that does not rely on a custom language grammar. Any HTML file is a valid Marko template and vice-versa, and the Marko compiler uses an [off-the-shelf HTML parser](https://github.com/fb55/htmlparser2). Because Marko understands the HTML structure of the templates, it can do more powerful things that would not be possible in a text-based templating languages such as Handlerbars, Dust or Mustache. Marko allows developers to _extend the HTML language_ by introducing custom HTML elements and attributes. On top of that, utilizing the HTML structure for applying templating directives makes templates more readable and allows data templates to more closely resemble the final HTML structure.
What makes Marko different is that it is an HTML-based templating language that allows [javascript expressions as attribute values](https://github.com/philidem/htmljs-parser). Any HTML file is a valid Marko template. Because Marko understands the HTML structure of the templates, it can support powerful functionality that would not be possible in text-based templating languages such as Handlerbars, Dust or Mustache. Marko allows developers to _extend the HTML language_ by introducing custom HTML elements and attributes. On top of that, utilizing the HTML structure for applying templating directives makes templates more readable and allows data templates to more closely resemble the final HTML structure.
Let's compare Marko with Handlebars (a text-based templating language):

View File

@ -81,6 +81,12 @@ exports.install = function(options) {
var compilerOptions = options.compilerOptions;
if (compilerOptions) {
require('./compiler').configure(compilerOptions);
} else {
compilerOptions = {};
}
var extension = options.extension || '.marko';
if (extension.charAt(0) !== '.') {

View File

@ -32,7 +32,7 @@
"dependencies": {
"app-module-path": "^1.0.5",
"async-vdom-builder": "^1.0.0",
"async-writer": "^1.4.4",
"async-writer": "^2.0.0",
"browser-refresh-client": "^1.0.0",
"char-props": "~0.1.5",
"deresolve": "^1.0.0",
@ -70,6 +70,7 @@
"jshint": "^2.5.0",
"mocha": "^2.3.3",
"request": "^2.72.0",
"require-self-ref": "^2.0.1",
"through": "^2.3.4"
},
"license": "Apache-2.0",

View File

@ -267,7 +267,7 @@ function createInlineMarkoTemplate(filename, renderFunc) {
exports.load = load;
exports.createWriter = function(writer) {
return new AsyncStream(writer);
return new AsyncStream(null, writer);
};
exports.helpers = helpers;

View File

@ -0,0 +1,17 @@
'use strict';
module.exports = function codeGenerator(el, codegen) {
let argument = el.argument;
if (!argument) {
return;
}
let builder = codegen.builder;
let pathExpression = builder.parseExpression(argument);
if (pathExpression.type !== 'Literal' || typeof pathExpression.value !== 'string') {
codegen.addError('Argument to the <include-text> tag should be a string value: <include-text("./foo.txt")/>');
return;
}
var path = pathExpression.value;
return builder.text(builder.literal('<include-html> cannot be compiled in the browser (path="' + path + '")'));
};

View File

@ -0,0 +1,30 @@
'use strict';
var resolveFrom = require('resolve-from');
var fs = require('fs');
module.exports = function codeGenerator(el, codegen) {
let argument = el.argument;
if (!argument) {
return;
}
let builder = codegen.builder;
let pathExpression = builder.parseExpression(argument);
if (pathExpression.type !== 'Literal' || typeof pathExpression.value !== 'string') {
codegen.addError('Argument to the <include-html> tag should be a string value: <include-html("./foo.txt")/>');
return;
}
var path = pathExpression.value;
var dirname = codegen.context.dirname;
try {
path = resolveFrom(dirname, path);
} catch(e) {
codegen.addError('File not found: ' + path);
return;
}
var txt = fs.readFileSync(path, { encoding: 'utf8' });
return builder.text(builder.literal(txt), false /* do not escape since this is HTML*/);
};

View File

@ -88,9 +88,27 @@
}
]
},
"<include-html>": {
"code-generator": "./include-html-tag",
"autocomplete": [
{
"displayText": "include-html(<path>)",
"snippet": "include-html(${1:\"./foo.html\"})",
"descriptionMoreURL": "http://markojs.com/docs/marko/language-guide/#includes"
},
{
"descriptionMoreURL": "http://markojs.com/docs/marko/language-guide/#includes"
}
]
},
"<include-text>": {
"code-generator": "./include-text-tag",
"autocomplete": [
{
"displayText": "include-text(<path>)",
"snippet": "include-text(${1:\"./foo.txt\"})",
"descriptionMoreURL": "http://markojs.com/docs/marko/language-guide/#includes"
},
{
"descriptionMoreURL": "http://markojs.com/docs/marko/language-guide/#includes"
}

View File

@ -1,5 +1,6 @@
{
"browser": {
"./include-text-tag.js": "./include-text-tag-browser.js"
"./include-text-tag.js": "./include-text-tag-browser.js",
"./include-html-tag.js": "./include-html-tag-browser.js"
}
}

View File

@ -1,6 +1,7 @@
'use strict';
require('./patch-module');
require('marko/node-require').install();
require('require-self-ref');
var chai = require('chai');
chai.config.includeStack = true;

View File

@ -0,0 +1,24 @@
exports.check = function(marko, markoCompiler, expect, done) {
markoCompiler.configure({
writeToDisk: true,
preserveWhitespace: true
}); // Reconfigure for testing
expect(markoCompiler.config.writeToDisk).to.equal(true);
expect(markoCompiler.config.preserveWhitespace).to.equal(true);
require('~/node-require').install({
compilerOptions: {
writeToDisk: false,
preserveWhitespace: false
}
});
expect(markoCompiler.config.writeToDisk).to.equal(false);
expect(markoCompiler.config.preserveWhitespace).to.equal(false);
markoCompiler.configure(); // Reset to defaults
expect(markoCompiler.config.writeToDisk).to.equal(true);
expect(markoCompiler.config.preserveWhitespace).to.equal(false);
done();
};

View File

@ -0,0 +1 @@
<b>red</b> &nbsp; <b>green</b> &nbsp; <b>blue</b>

View File

@ -0,0 +1,3 @@
<b for(item in ['red', 'green', 'blue'] | separator=' &nbsp; ')>
${item}
</b>

View File

@ -0,0 +1 @@
exports.templateData = {};

View File

@ -0,0 +1,5 @@
BEGIN <div>
<h1>
Hello World!
</h1>
</div> END

View File

@ -0,0 +1,5 @@
<div>
<h1>
Hello World!
</h1>
</div>

View File

@ -0,0 +1,5 @@
---
BEGIN
<include-html('./include-html-target.html')/>
END
---

View File

@ -0,0 +1 @@
exports.templateData = {};

View File

@ -8,6 +8,7 @@
"for",
"if",
"include",
"include-html",
"include-text",
"invoke",
"macro",

View File

@ -21,6 +21,7 @@
"html-comment",
"if",
"include",
"include-html",
"include-text",
"invoke",
"layout-placeholder",