Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

riot-compiler

Package Overview
Dependencies
Maintainers
4
Versions
50
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

riot-compiler - npm Package Compare versions

Comparing version 2.3.13 to 2.3.16-beta

dist/es6.compiler.js

59

CHANGELOG.md

@@ -1,21 +0,50 @@

# COMPILER CHANGES
# Compiler Changes
## v2.3.12
### v2.3.16
- Regression of optimized regexes not working in IE9/10.
- Fix #36 : removed the excluded strings from the ouput.
- Fix: avoid changing the global brackets when the compiler is called with other brackets (requires riot-tmpl v2.3.17).
- Preparation for recognize the raw-html flag `=` (can change in the final implementation).
- A new property `version` (string) is included in the compiler set.
### v2.3.15 (unpublished from npm)
- Preparation for use as ES6 module through [rollup.js](http://rollupjs.org/)
- Update devDependencies, including jspreproc v0.2.5 with an important fix.
- Partial regression of fix [riot#1120](https://github.com/riot/riot/issues/1120), `tmpl` can parse double-quotes within expressions, encoding double-quotes generates issues.
### v2.3.14
- The prefix `__` for boolean attributes is not used anymore. This IE8 hack and it is not neccessary for current supported versions.
- Option `exclude` for ignore parts of the tag. This is an array with one or more of 'html', 'css', 'attribs', 'js'.
- Removed `inert` from the boolean attributes list, this html5 attribute was dropped from the specs.
- Fixed normalization of root attributes, was not working as expected. Example updated.
### v2.3.13
- Fixed the `style` option for setting the CSS parser through the `options` object.
- Fixed an issue in preservation of line endings in the generated html markup.
- Fixed tests, coverage is 100% again.
- Updated [doc/guide.md](https://github.com/riot/compiler/blob/master/doc/guide.md) and [doc/attributes.md](https://github.com/riot/compiler/blob/master/doc/attrbutes.md) with the latest features.
### v2.3.12
- Gets rid of the zero-indentation restriction for custom tags, now you can indent these tags, but the opening and closing tag must have exactly the same indentation (length and type). All the tag will be unindented by this amount.
- Support for `src` and `charset` attributes in `<script>` tags for reading JavaScript sources from the file system - [riot#507](https://github.com/riot/riot/issues/507)
- Support for `src` and `charset` attributes in `<script>` tags for reading JavaScript sources from the file system - [riot#1116](https://github.com/riot/riot/issues/1116), [riot#507](https://github.com/riot/riot/issues/507)
- The `compile` function can return separate parts by setting the new `entities` option. These parts has unescaped newlines.
- New attribute `options` for `script` and `style` tags will append/overwrite attributes in the default configuration object of the parser at tag level.
- Fix [riot#1261](https://github.com/riot/riot/issues/1261): `<pre>` tag does not preserve neither `\n` nor `\t`.
- Fix [riot#1261](https://github.com/riot/riot/issues/1261) : `<pre>` tag does not preserve neither `\n` nor `\t`.
Now whitespace within `<pre>` tags is always preserved.
- Fix [riot#1358](https://github.com/riot/riot/issues/1358): Empty style in tag (scoped) breaks.
- Fix [riot#1358](https://github.com/riot/riot/issues/1358) : Empty style in tag (scoped) breaks.
## v2.3.11
### v2.3.11
- New type="babel" supports babel-core v6.x. You must `npm install babel-preset-es2015` too, for this works.
Use type="es6" for babel and babel-core v5.8.x and bellow.
- Fix [riot#1306](https://github.com/riot/riot/issues/1306): Compiler preserves newlines in class objects, causing "Unterminated String Constant" errors.
- Fix [riot#1314](https://github.com/riot/riot/issues/1314): `settings.brackets` no longer works.
Use type="es6" for babel and babel-core v5.8.x and bellow - [riot#1039](https://github.com/riot/riot/issues/1039)
- Fix [riot#1306](https://github.com/riot/riot/issues/1306) : Compiler preserves newlines in class objects, causing "Unterminated String Constant" errors.
- Fix [riot#1314](https://github.com/riot/riot/issues/1314) : `settings.brackets` no longer works.
- Fix [riot#1309](https://github.com/riot/riot/issues/1309) : Tag renders js instead of content when no attributes present.
## v2.3.0
### v2.3.0

@@ -29,9 +58,9 @@ This is a complete rewrite and the first solo version of the compiler.

Outside of expressions, all backslashes are preserved.
- Double quotes inside expressions are converted to `&quot;`, to avoid issues with HTML markup
- Double quotes inside expressions are converted to `&quot;`, to avoid issues with HTML markup.
- Fix [riot#1207](https://github.com/riot/riot/issues/1207) : Riot compiler/parser breaks indentation.
- Fix [riot#1120](https://github.com/riot/riot/issues/1120) : Double quotes break Riot attributes
- Fix [riot#1207](https://github.com/riot/riot/issues/1207): Riot compiler/parser breaks indentation.
- Fix [riot#1120](https://github.com/riot/riot/issues/1120): Double quotes break Riot attributes
Enhancements
- The compiler loads the brackets in runtime on each tag, allowing use of different brackets. [riot#1122](https://github.com/riot/riot/issues/1122) related.
- Multiple `<script>` blocks. These can have different types and are merged with the untagged script block, if any.

@@ -41,5 +70,5 @@ - More flexible formats in ES6 style method definitions.

- Better recognition of expressions. Now you can use almost any character, even in unquoted expressions (expressions containing the `>` operator needs to be enclosed in quotes) - [riot#744](https://github.com/riot/riot/issues/744)
- If the first character inside an expression is `^`, the expression is not passed to any parser. This is some sort of type=none at expression level - [riot#543](https://github.com/riot/riot/issues/543) and [riot#1090](https://github.com/riot/riot/issues/1090)
- If the first character inside an expression is `^`, the expression is not passed to any parser. This is some sort of type=none at expression level - [riot#543](https://github.com/riot/riot/issues/543), [riot#1090](https://github.com/riot/riot/issues/1090)
- Type es6 now supports babel-core - [riot#1039](https://github.com/riot/riot/issues/1039)
- New logic for scoped style blocks, if a style contains the ":scoped" selector, this is replaced by the name of the root element, if not, the name is prepended - [riot#912](https://github.com/riot/riot/issues/912)
- `type="scoped-css"` for `style` tags is deprecated, use only `scoped` or `scoped="scoped"`

@@ -1,154 +0,147 @@

/* riot-compiler v2.3.13, @license MIT, (c) 2015 Muut Inc. + contributors */
;(function (root, factory) {
/* riot-compiler v2.3.16-beta, @license MIT, (c) 2015 Muut Inc. + contributors */
'use strict' // eslint-disable-line
/* istanbul ignore else */
if (typeof module === 'object' && module.exports) {
module.exports = factory(require('riot-tmpl'))
}
else if (typeof define === 'function' && define.amd) {
define(['riot-tmpl'], factory)
}
else if (root) {
root.compiler = factory(root.riot.util)
}
/**
* @module parsers
*/
var parsers = (function () {
var _mods = {}
})(this, function (_tmpl) {
'use strict' // eslint-disable-line
function _try(name, req) { //eslint-disable-line complexity
var parser
/**
* @module parsers
*/
var parsers = (function () {
var _mods = {}
function _try(name, req) { //eslint-disable-line complexity
function fn(r) {
try {
_mods[name] = require(r)
}
catch (e) {
_mods[name] = null
}
return _mods[name]
function fn(r) {
try {
_mods[name] = require(r)
}
switch (name) {
case 'es6':
/* istanbul ignore next */
return fn('babel') || fn('babel-core')
case 'babel':
req = 'babel-core'
break
case 'none':
case 'javascript':
return _js.none
case 'typescript':
req = name + '-simple'
break
case 'coffee':
case 'coffeescript':
req = 'coffee-script'
break
/* istanbul ignore next */
case 'scss':
case 'sass':
req = 'node-sass'
break
default:
if (!req) req = name
break
catch (e) {
_mods[name] = null
}
return fn(req)
return _mods[name]
}
function _req(name, req) {
return name in _mods ? _mods[name] : _try(name, req)
switch (name) {
case 'es6':
/* istanbul ignore next */
return fn('babel') || fn('babel-core')
case 'babel':
req = 'babel-core'
break
case 'none':
case 'javascript':
return _js.none
case 'typescript':
req = name + '-simple'
break
case 'coffee':
case 'coffeescript':
req = 'coffee-script'
break
case 'scss':
case 'sass':
req = 'node-sass'
break
default:
if (!req) req = name
break
}
parser = fn(req)
var _html = {
jade: function (html, opts) {
return _req('jade').render(html, extend({pretty: true, doctype: 'html'}, opts))
}
return parser
}
function _req(name, req) {
return name in _mods ? _mods[name] : _try(name, req)
}
var _html = {
jade: function (html, opts, url) {
return _req('jade').render(html, extend({
pretty: true,
filename: url,
doctype: 'html'
}, opts))
}
}
var _css = {
sass: function(tag, css, opts) {
var sass = _req('sass')
var _css = {
sass: function(tag, css, opts, url) {
var sass = _req('sass')
return sass.renderSync(extend({
data: css,
indentedSyntax: true,
omitSourceMapUrl: true,
outputStyle: 'compact'
}, opts)).css + ''
},
scss: function(tag, css, opts) {
var sass = _req('sass')
return sass.renderSync(extend({
data: css,
indentedSyntax: true,
omitSourceMapUrl: true,
outputStyle: 'compact'
}, opts)).css + ''
},
scss: function(tag, css, opts, url) {
var sass = _req('sass')
return sass.renderSync(extend({
data: css,
indentedSyntax: false,
omitSourceMapUrl: true,
outputStyle: 'compact'
}, opts)).css + ''
},
less: function(tag, css, opts) {
var less = _req('less'),
ret
return sass.renderSync(extend({
data: css,
indentedSyntax: false,
omitSourceMapUrl: true,
outputStyle: 'compact'
}, opts)).css + ''
},
less: function(tag, css, opts, url) {
var less = _req('less'),
ret
less.render(css, extend({
sync: true,
compress: true
}, opts), function (err, result) {
// istanbul ignore next
if (err) throw err
ret = result.css
})
return ret
},
stylus: function (tag, css, opts) {
var
stylus = _req('stylus'), nib = _req('nib')
/* istanbul ignore next: can't run both */
return nib ?
stylus(css).use(nib()).import('nib').render() : stylus.render(css)
}
less.render(css, extend({
sync: true,
compress: true
}, opts), function (err, result) {
// istanbul ignore next
if (err) throw err
ret = result.css
})
return ret
},
stylus: function (tag, css, opts, url) {
var
stylus = _req('stylus'), nib = _req('nib')
/* istanbul ignore next: can't run both */
return nib ?
stylus(css).use(nib()).import('nib').render() : stylus.render(css)
}
}
var _js = {
none: function (js, opts) {
return js
},
livescript: function (js, opts) {
return _req('livescript').compile(js, extend({bare: true, header: false}, opts))
},
typescript: function (js, opts) {
return _req('typescript')(js, opts).replace(/\r\n?/g, '\n')
},
es6: function (js, opts) {
return _req('es6').transform(js, extend({
blacklist: ['useStrict', 'strict', 'react'], sourceMaps: false, comments: false
}, opts)).code
},
babel: function (js, opts) {
js = 'function __parser_babel_wrapper__(){' + js + '}'
return _req('babel').transform(js,
extend({
presets: ['es2015']
}, opts)
).code.replace(/["']use strict["'];[\r\n]+/, '').slice(38, -2)
},
coffee: function (js, opts) {
return _req('coffee').compile(js, extend({bare: true}, opts))
}
var _js = {
none: function (js, opts, url) {
return js
},
livescript: function (js, opts, url) {
return _req('livescript').compile(js, extend({bare: true, header: false}, opts))
},
typescript: function (js, opts, url) {
return _req('typescript')(js, opts).replace(/\r\n?/g, '\n')
},
es6: function (js, opts, url) {
return _req('es6').transform(js, extend({
blacklist: ['useStrict', 'strict', 'react'], sourceMaps: false, comments: false
}, opts)).code
},
babel: function (js, opts, url) {
return _req('babel').transform(js,
extend({
filename: url
}, opts)
).code
},
coffee: function (js, opts, url) {
return _req('coffee').compile(js, extend({bare: true}, opts))
}
}
_js.javascript = _js.none
_js.coffeescript = _js.coffee
_js.javascript = _js.none
_js.coffeescript = _js.coffee
return {html: _html, css: _css, js: _js, _req: _req}
return {html: _html, css: _css, js: _js, _req: _req}
})()
})()
var brackets = require('riot-tmpl').brackets
/**

@@ -158,459 +151,478 @@ * @module compiler

var brackets = _tmpl.brackets //eslint-disable-line no-redeclare
// istanbul ignore next
if (!brackets.version) {
throw new Error('This compiler version requires riot-tmpl v2.3.16 or above')
}
function _regEx(str, opt) { return new RegExp(str, opt) }
function _regEx(str, opt) { return new RegExp(str, opt) }
var
var
BOOL_ATTRS = _regEx(
'^(?:disabled|checked|readonly|required|allowfullscreen|auto(?:focus|play)|' +
'compact|controls|default|formnovalidate|hidden|inert|ismap|itemscope|loop|' +
'multiple|muted|no(?:resize|shade|validate|wrap)?|open|reversed|seamless|' +
'selected|sortable|truespeed|typemustmatch)$'),
BOOL_ATTRS = _regEx(
'^(?:disabled|checked|readonly|required|allowfullscreen|auto(?:focus|play)|' +
'compact|controls|default|formnovalidate|hidden|ismap|itemscope|loop|' +
'multiple|muted|no(?:resize|shade|validate|wrap)?|open|reversed|seamless|' +
'selected|sortable|truespeed|typemustmatch)$'),
RIOT_ATTRS = ['style', 'src', 'd'],
RIOT_ATTRS = ['style', 'src', 'd'],
VOID_TAGS = /^(?:input|img|br|wbr|hr|area|base|col|embed|keygen|link|meta|param|source|track)$/,
VOID_TAGS = /^(?:input|img|br|wbr|hr|area|base|col|embed|keygen|link|meta|param|source|track)$/,
HTML_ATTR = /\s*([-\w:\.\xA0-\xFF]+)\s*(?:=\s*('[^']+'|"[^"]+"|\S+))?/g,
HTML_ATTR = /\s*([-\w:\xA0-\xFF]+)\s*(?:=\s*('[^']+'|"[^"]+"|\S+))?/g,
TRIM_TRAIL = /[ \t]+$/gm,
TRIM_TRAIL = /[ \t]+$/gm
_bp = null
var path = require('path')
var path = require('path')
function q(s) {
return "'" + (s ? s
.replace(/\\/g, '\\\\').replace(/'/g, "\\'").replace(/\n/g, '\\n').replace(/\r/g, '\\r') :
'') + "'"
}
function q(s) {
return "'" + (s ? s
.replace(/\\/g, '\\\\').replace(/'/g, "\\'").replace(/\n/g, '\\n').replace(/\r/g, '\\r') :
'') + "'"
}
function mktag(name, html, css, attrs, js, pcex) {
var
c = ', ',
s = '}' + (pcex.length ? ', ' + q(pcex._bp[8]) : '') + ');'
function mktag(name, html, css, attrs, js, pcex) {
var
c = ', ',
s = '}' + (pcex.length ? ', ' + q(_bp[8]) : '') + ');'
if (js && js.slice(-1) !== '\n') s = '\n' + s
if (js && js.slice(-1) !== '\n') s = '\n' + s
return 'riot.tag2(\'' + name + "'" + c + q(html) + c + q(css) + c + q(attrs) +
', function(opts) {\n' + js + s
}
return 'riot.tag2(\'' + name + "'" + c + q(html) + c + q(css) + c + q(attrs) +
', function(opts) {\n' + js + s
}
function extend(obj, props) {
for (var prop in props) {
/* istanbul ignore next */
if (props.hasOwnProperty(prop)) {
obj[prop] = props[prop]
}
function extend(obj, props) {
for (var prop in props) {
/* istanbul ignore next */
if (props.hasOwnProperty(prop)) {
obj[prop] = props[prop]
}
return obj
}
return obj
}
function parseAttrs(str) {
var
list = [],
match,
k, v,
DQ = '"'
HTML_ATTR.lastIndex = 0
function parseAttrs(str, pcex) {
var
list = [],
match,
k, v,
_bp = pcex._bp,
DQ = '"'
while (match = HTML_ATTR.exec(str)) {
HTML_ATTR.lastIndex = 0
k = match[1].toLowerCase()
v = match[2]
str = str.replace(/\s+/g, ' ')
if (!v) {
list.push(k)
}
else {
while (match = HTML_ATTR.exec(str)) {
if (v[0] !== DQ)
v = DQ + (v[0] === "'" ? v.slice(1, -1) : v) + DQ
k = match[1].toLowerCase()
v = match[2]
if (k === 'type' && v.toLowerCase() === '"number"') {
v = DQ + _bp[0] + "'number'" + _bp[1] + DQ
}
else if (/\u0001\d/.test(v)) {
if (!v) {
list.push(k)
}
else {
if (BOOL_ATTRS.test(k)) {
k = '__' + k
}
else if (~RIOT_ATTRS.indexOf(k)) {
k = 'riot-' + k
}
if (v[0] !== DQ)
v = DQ + (v[0] === "'" ? v.slice(1, -1) : v) + DQ
if (k === 'type' && v.toLowerCase() === '"number"') {
v = DQ + _bp[0] + "'number'" + _bp[1] + DQ
}
else if (/\u0001\d/.test(v)) {
if (BOOL_ATTRS.test(k)) {
k = '__' + k
}
else if (~RIOT_ATTRS.indexOf(k)) {
k = 'riot-' + k
}
}
list.push(k + '=' + v)
}
list.push(k + '=' + v)
}
return list.join(' ')
}
return list.join(' ')
}
function splitHtml(html, opts, pcex) {
function splitHtml(html, opts, pcex) {
var _bp = pcex._bp
if (html && _bp[4].test(html)) {
var
jsfn = opts.expr && (opts.parser || opts.type) ? compileJS : 0,
list = brackets.split(html),
expr
if (html && _bp[4].test(html)) {
var
jsfn = opts.expr && (opts.parser || opts.type) ? compileJS : 0,
list = brackets.split(html, 0, _bp),
expr
for (var i = 1; i < list.length; i += 2) {
expr = list[i]
if (expr[0] === '^')
expr = expr.slice(1)
else if (jsfn) {
expr = jsfn(expr, opts)
if (/;\s*$/.test(expr)) expr = expr.slice(0, expr.search(/;\s*$/))
}
list[i] = '\u0001' + (pcex.push(expr.replace(/[\r\n]+/g, ' ').trim()) - 1) + _bp[1]
for (var i = 1; i < list.length; i += 2) {
expr = list[i]
if (expr[0] === '^')
expr = expr.slice(1)
else if (jsfn) {
var israw = expr[0] === '='
expr = jsfn(israw ? expr.slice(1) : expr, opts).trim()
if (expr.slice(-1) === ';') expr = expr.slice(0, -1)
if (israw) expr = '=' + expr
}
html = list.join('')
list[i] = '\u0001' + (pcex.push(expr.replace(/[\r\n]+/g, ' ').trim()) - 1) + _bp[1]
}
return html
html = list.join('')
}
return html
}
function restoreExpr(html, pcex) {
if (pcex.length) {
html = html
.replace(/\u0001(\d+)/g, function (_, d) {
return _bp[0] + pcex[d].replace(/"/g, '&quot;')
})
}
return html
function restoreExpr(html, pcex) {
if (pcex.length) {
html = html
.replace(/\u0001(\d+)/g, function (_, d) {
var expr = pcex[d]
if (expr[0] === '=') {
expr = expr.replace(brackets.R_STRINGS, function (qs) {
return qs
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
})
}
return pcex._bp[0] + expr
})
}
return html
}
var
HTML_COMMENT = /<!--(?!>)[\S\s]*?-->/g,
HTML_TAGS = /<([-\w]+)\s*([^"'\/>]*(?:(?:"[^"]*"|'[^']*'|\/[^>])[^'"\/>]*)*)(\/?)>/g
var
HTML_COMMENT = /<!--(?!>)[\S\s]*?-->/g,
HTML_TAGS = /<([-\w]+)\s*([^"'\/>]*(?:(?:"[^"]*"|'[^']*'|\/[^>])[^'"\/>]*)*)(\/?)>/g,
PRE_TAG = _regEx(
/<pre(?:\s+[^'">]+(?:(?:@Q)[^'">]*)*|\s*)?>([\S\s]*?)<\/pre\s*>/
.source.replace('@Q', brackets.R_STRINGS.source), 'gi')
function compileHTML(html, opts, pcex, intc) {
function compileHTML(html, opts, pcex ) {
if (!intc) {
_bp = brackets.array(opts.brackets)
html = html.replace(/\r\n?/g, '\n').replace(HTML_COMMENT, '').replace(TRIM_TRAIL, '')
}
if (!pcex) pcex = []
var intf = (pcex || (pcex = []))._intflag
if (!intf)
html = html.replace(/\r\n?/g, '\n').replace(HTML_COMMENT, '').replace(TRIM_TRAIL, '')
html = splitHtml(html, opts, pcex)
.replace(HTML_TAGS, function (_, name, attr, ends) {
if (!pcex._bp) pcex._bp = brackets.array(opts.brackets)
name = name.toLowerCase()
html = splitHtml(html, opts, pcex)
.replace(HTML_TAGS, function (_, name, attr, ends) {
ends = ends && !VOID_TAGS.test(name) ? '></' + name : ''
name = name.toLowerCase()
if (attr) name += ' ' + parseAttrs(attr)
ends = ends && !VOID_TAGS.test(name) ? '></' + name : ''
return '<' + name + ends + '>'
})
if (attr) name += ' ' + parseAttrs(attr, pcex)
if (!opts.whitespace) {
var p = [],
pre = /<pre(?:\s+[^'">]+(?:(?:"[^"]*"|'[^']*')[^'">]*)*|\s*)>[\s\S]*<\/pre\s*>/gi
return '<' + name + ends + '>'
})
html = html.replace(pre, function (q) {
return '\u0002' + (p.push(q) - 1) + '~' }).trim().replace(/\s+/g, ' ')
if (!opts.whitespace) {
if (/<pre[\s>]/.test(html)) {
var p = []
html = html.replace(PRE_TAG, function (q)
{ return p.push(q) && '\u0002' }).trim().replace(/\s+/g, ' ')
// istanbul ignore else
if (p.length)
html = html.replace(/\u0002(\d+)~/g, function (q, n) { return p[n] })
html = html.replace(/\u0002/g, function (_) { return p.shift() })
}
if (opts.compact) html = html.replace(/> <([-\w\/])/g, '><$1')
return restoreExpr(html, pcex)
else
html = html.trim().replace(/\s+/g, ' ')
}
var
if (opts.compact) html = html.replace(/> <([-\w\/])/g, '><$1')
JS_RMCOMMS = _regEx(
'(' + brackets.S_QBLOCKS + ')|' + brackets.R_MLCOMMS.source + '|//[^\r\n]*',
'g'),
return restoreExpr(html, pcex)
}
JS_ES6SIGN = /^([ \t]*)([$_A-Za-z][$\w]*)\s*(\([^()]*\)\s*{)/m
var
JS_RMCOMMS = _regEx('(' + brackets.S_QBLOCKS + ')|' + brackets.R_MLCOMMS.source + '|//[^\r\n]*', 'g'),
JS_ES6SIGN = /^([ \t]*)([$_A-Za-z][$\w]*)\s*(\([^()]*\)\s*{)/m
function riotjs(js) {
var
match,
toes5,
parts = [],
pos
function riotjs(js) {
var
match,
toes5,
parts = [],
pos
js = js.replace(JS_RMCOMMS, function (m, q) { return q ? m : ' ' })
js = js.replace(JS_RMCOMMS, function (m, q) { return q ? m : ' ' })
while (match = js.match(JS_ES6SIGN)) {
while (match = js.match(JS_ES6SIGN)) {
parts.push(RegExp.leftContext)
js = RegExp.rightContext
pos = skipBlock(js)
parts.push(RegExp.leftContext)
js = RegExp.rightContext
pos = skipBlock(js)
toes5 = !/^(?:if|while|for|switch|catch|function)$/.test(match[2])
if (toes5)
match[0] = match[1] + 'this.' + match[2] + ' = function' + match[3]
toes5 = !/^(?:if|while|for|switch|catch|function)$/.test(match[2])
if (toes5)
match[0] = match[1] + 'this.' + match[2] + ' = function' + match[3]
parts.push(match[0], js.slice(0, pos))
js = js.slice(pos)
if (toes5 && !/^\s*.\s*bind\b/.test(js)) parts.push('.bind(this)')
}
parts.push(match[0], js.slice(0, pos))
js = js.slice(pos)
if (toes5 && !/^\s*.\s*bind\b/.test(js)) parts.push('.bind(this)')
}
return parts.length ? parts.join('') + js : js
return parts.length ? parts.join('') + js : js
function skipBlock(str) {
var
re = _regEx('([{}])|' + brackets.S_QBLOCKS, 'g'),
level = 1,
match
function skipBlock(str) {
var
re = _regEx('([{}])|' + brackets.S_QBLOCKS, 'g'),
level = 1,
match
while (level && (match = re.exec(str))) {
if (match[1])
match[1] === '{' ? ++level : --level
}
return level ? str.length : re.lastIndex
while (level && (match = re.exec(str))) {
if (match[1])
match[1] === '{' ? ++level : --level
}
return level ? str.length : re.lastIndex
}
}
function compileJS(js, opts, type, parserOpts) {
if (!js) return ''
if (!type) type = opts.type
function compileJS(js, opts, type, parserOpts, url) {
if (!js) return ''
if (!type) type = opts.type
var parser = opts.parser || (type ? parsers.js[type] : riotjs)
if (!parser)
throw new Error('JS parser not found: "' + type + '"')
var parser = opts.parser || (type ? parsers.js[type] : riotjs)
if (!parser)
throw new Error('JS parser not found: "' + type + '"')
return parser(js, parserOpts).replace(TRIM_TRAIL, '')
}
return parser(js, parserOpts, url).replace(TRIM_TRAIL, '')
}
var CSS_SELECTOR = _regEx('(}|{|^)[ ;]*([^@ ;{}][^{}]*)(?={)|' + brackets.R_STRINGS.source, 'g')
var CSS_SELECTOR = _regEx('(}|{|^)[ ;]*([^@ ;{}][^{}]*)(?={)|' + brackets.R_STRINGS.source, 'g')
function scopedCSS(tag, style) {
var scope = ':scope'
function scopedCSS(tag, style) {
var scope = ':scope'
return style.replace(CSS_SELECTOR, function (m, p1, p2) {
return style.replace(CSS_SELECTOR, function (m, p1, p2) {
if (!p2) return m
if (!p2) return m
p2 = p2.replace(/[^,]+/g, function (sel) {
var s = sel.trim()
p2 = p2.replace(/[^,]+/g, function (sel) {
var s = sel.trim()
if (s && s !== 'from' && s !== 'to' && s.slice(-1) !== '%') {
if (s && s !== 'from' && s !== 'to' && s.slice(-1) !== '%') {
if (s.indexOf(scope) < 0) s = scope + ' ' + s
s = s.replace(scope, tag) + ',' +
s.replace(scope, '[riot-tag="' + tag + '"]')
}
return sel.slice(-1) === ' ' ? s + ' ' : s
})
return p1 ? p1 + ' ' + p2 : p2
if (s.indexOf(scope) < 0) s = scope + ' ' + s
s = s.replace(scope, tag) + ',' +
s.replace(scope, '[riot-tag="' + tag + '"]')
}
return sel.slice(-1) === ' ' ? s + ' ' : s
})
}
function compileCSS(style, tag, type, scoped, opts) {
return p1 ? p1 + ' ' + p2 : p2
})
}
if (type) {
if (type === 'scoped-css') {
scoped = true
}
else if (parsers.css[type]) {
style = parsers.css[type](tag, style, opts)
}
else if (type !== 'css') {
throw new Error('CSS parser not found: "' + type + '"')
}
function compileCSS(style, tag, type, scoped, opts) {
if (type) {
if (type === 'scoped-css') {
scoped = true
}
else if (parsers.css[type]) {
style = parsers.css[type](tag, style, opts)
}
else if (type !== 'css') {
throw new Error('CSS parser not found: "' + type + '"')
}
}
style = style.replace(brackets.R_MLCOMMS, '').replace(/\s+/g, ' ').trim()
style = style.replace(brackets.R_MLCOMMS, '').replace(/\s+/g, ' ').trim()
return scoped ? scopedCSS(tag, style) : style
}
return scoped ? scopedCSS(tag, style) : style
}
var
TYPE_ATTR = /\stype\s*=\s*(?:(['"])(.+?)\1|(\S+))/i,
MISC_ATTR = /\s*=\s*("(?:\\[\S\s]|[^"\\]*)*"|'(?:\\[\S\s]|[^'\\]*)*'|\{[^}]+}|\S+)/.source
var
TYPE_ATTR = /\stype\s*=\s*(?:(['"])(.+?)\1|(\S+))/i,
MISC_ATTR = /\s*=\s*("(?:\\[\S\s]|[^"\\]*)*"|'(?:\\[\S\s]|[^'\\]*)*'|\{[^}]+}|\S+)/.source
function getType(str) {
function getType(str) {
if (str) {
var match = str.match(TYPE_ATTR)
str = match && (match[2] || match[3])
}
return str ? str.replace('text/', '') : ''
if (str) {
var match = str.match(TYPE_ATTR)
str = match && (match[2] || match[3])
}
return str ? str.replace('text/', '') : ''
}
function getAttr(str, name) {
function getAttr(str, name) {
if (str) {
var
re = _regEx('\\s' + name + MISC_ATTR, 'i'),
match = str.match(re)
str = match && match[1]
if (str)
return /^['"]/.test(str) ? str.slice(1, -1) : str
}
return ''
if (str) {
var
re = _regEx('\\s' + name + MISC_ATTR, 'i'),
match = str.match(re)
str = match && match[1]
if (str)
return (/^['"]/).test(str) ? str.slice(1, -1) : str
}
return ''
}
// get the parser options from the options attribute
function getParserOptions(attrs) {
var opts = getAttr(attrs, 'options')
// convert the string into a valid js object
if (opts) opts = JSON.parse(opts)
return opts
}
function getParserOptions(attrs) {
var opts = getAttr(attrs, 'options')
// Runs the custom or default parser on the received JavaScript code.
// The CLI version can read code from the file system (experimental)
function getCode(code, opts, attrs, url) {
var type = getType(attrs),
parserOpts = getParserOptions(attrs)
if (opts) opts = JSON.parse(opts)
return opts
}
//#if READ_JS_SRC
function getCode(code, opts, attrs, url) {
var type = getType(attrs),
parserOpts = getParserOptions(attrs)
if (url) {
var src = getAttr(attrs, 'src')
if (src && url) {
if (src) {
var
charset = getAttr(attrs, 'charset'),
file = path.resolve(path.dirname(url), src)
code = require('fs').readFileSync(file, {encoding: charset || 'utf8'})
code = require('fs').readFileSync(file, charset || 'utf8')
}
//#endif
return compileJS(code, opts, type, parserOpts)
}
return compileJS(code, opts, type, parserOpts, url)
}
// Matches HTML tag ending a line. This regex still can be fooled by code as:
// ```js
// x <y && y >
// z
// ```
var END_TAGS = /\/>\n|^<(?:\/[\w\-]+\s*|[\w\-]+(?:\s+(?:[-\w:\xA0-\xFF][\S\s]*?)?)?)>\n/
var END_TAGS = /\/>\n|^<(?:\/[\w\-]+\s*|[\w\-]+(?:\s+(?:[-\w:\xA0-\xFF][\S\s]*?)?)?)>\n/
function splitBlocks(str) {
var k, m
function splitBlocks(str) {
var k, m
/* istanbul ignore next: this if() can't be true, but just in case... */
if (str[str.length - 1] === '>')
return [str, '']
/* istanbul ignore next: this if() can't be true, but just in case... */
if (str[str.length - 1] === '>') return [str, '']
k = str.lastIndexOf('<') // first probable open tag
while (~k) {
if (m = str.slice(k).match(END_TAGS)) {
k += m.index + m[0].length
return [str.slice(0, k), str.slice(k)]
}
k = str.lastIndexOf('<', k -1)
k = str.lastIndexOf('<')
while (~k) {
if (m = str.slice(k).match(END_TAGS)) {
k += m.index + m[0].length
return [str.slice(0, k), str.slice(k)]
}
return ['', str]
k = str.lastIndexOf('<', k -1)
}
return ['', str]
}
// Runs the external HTML parser for the entire tag file
function compileTemplate(lang, html, opts) {
var parser = parsers.html[lang]
function compileTemplate(html, url, lang, opts) {
var parser = parsers.html[lang]
if (!parser)
throw new Error('Template parser not found: "' + lang + '"')
if (!parser)
throw new Error('Template parser not found: "' + lang + '"')
return parser(html, opts)
}
return parser(html, opts, url)
}
/*
CUST_TAG regex don't allow unquoted expressions containing the `>` operator.
STYLE and SCRIPT disallows the operator `>` at all.
var
CUST_TAG = _regEx(
/^([ \t]*)<([-\w]+)(?:\s+([^'"\/>]+(?:(?:@Q|\/[^>])[^'"\/>]*)*)|\s*)?(?:\/>|>[ \t]*\n?([\S\s]*)^\1<\/\2\s*>|>(.*)<\/\2\s*>)/
.source.replace('@Q', brackets.R_STRINGS.source), 'gim'),
STYLE = /<style(\s+[^>]*)?>\n?([^<]*(?:<(?!\/style\s*>)[^<]*)*)<\/style\s*>/gi,
SCRIPT = _regEx(STYLE.source.replace(/tyle/g, 'cript'), 'gi')
The beta.4 CUST_TAG regex is fast, with RegexBuddy I get 76 steps and 14 backtracks on
the test/specs/fixtures/treeview.tag :) but fails with nested tags of the same name :(
With a greedy * operator, we have ~500 and 200bt, it is acceptable. So let's fix this.
*/
function compile(src, opts, url) {
var
CUST_TAG = /^([ \t]*)<([-\w]+)(?:\s+([^'"\/>]+(?:(?:"[^"]*"|'[^']*'|\/[^>])[^'"\/>]*)*)|\s*)?(?:\/>|>[ \t]*\n?([\s\S]*)^\1<\/\2\s*>|>(.*)<\/\2\s*>)/gim,
STYLE = /<style(\s+[^>]*)?>\n?([^<]*(?:<(?!\/style\s*>)[^<]*)*)<\/style\s*>/gi,
SCRIPT = _regEx(STYLE.source.replace(/tyle/g, 'cript'), 'gi')
parts = [],
exclude
function compile(src, opts, url) {
var label, parts = []
if (!opts) opts = {}
if (!opts) opts = {}
url = url || process.cwd()
_bp = brackets.array(opts.brackets)
exclude = opts.exclude || false
function included(s) { return !(exclude && ~exclude.indexOf(s)) }
if (opts.template)
src = compileTemplate(opts.template, src, opts.templateOptions)
var _bp = brackets.array(opts.brackets)
/* istanbul ignore next */
label = !url ? '' : path.isAbsolute(url) ? path.relative('.', url) : url
if (label)
label = '//src: ' + label.replace(/\\/g, '/') + '\n'
if (opts.template)
src = compileTemplate(src, url, opts.template, opts.templateOptions)
src = label + src
.replace(/\r\n?/g, '\n')
.replace(CUST_TAG, function (_, indent, tagName, attribs, body, body2) {
src = src
.replace(/\r\n?/g, '\n')
.replace(CUST_TAG, function (_, indent, tagName, attribs, body, body2) {
var
jscode = '',
styles = '',
html = '',
pcex = []
var
jscode = '',
styles = '',
html = '',
pcex = []
tagName = tagName.toLowerCase()
pcex._bp = _bp
pcex._intflag = 1
attribs = !attribs ? '' : restoreExpr(parseAttrs(splitHtml(attribs, opts, pcex)), pcex)
tagName = tagName.toLowerCase()
if (body2) body = body2
attribs = attribs && included('attribs') ?
restoreExpr(parseAttrs(splitHtml(attribs, opts, pcex), pcex), pcex) : ''
if (body && (body = body.replace(HTML_COMMENT, '')) && /\S/.test(body)) {
if (body2) body = body2
if (body2)
html = compileHTML(body2, opts, pcex, 1)
else {
body = body.replace(_regEx('^' + indent, 'gm'), '')
if (body && (body = body.replace(HTML_COMMENT, '')) && /\S/.test(body)) {
body = body.replace(STYLE, function (_, _attrs, _style) {
var scoped = _attrs && /\sscoped(\s|=|$)/i.test(_attrs),
csstype = getType(_attrs) || opts.style
styles += (styles ? ' ' : '') +
compileCSS(_style, tagName, csstype, scoped, getParserOptions(_attrs))
return ''
})
if (body2) {
/* istanbul ignore next */
html = included('html') ? compileHTML(body2, opts, pcex, 1, url) : ''
}
else {
body = body.replace(_regEx('^' + indent, 'gm'), '')
body = body.replace(SCRIPT, function (_, _attrs, _script) {
jscode += (jscode ? '\n' : '') + getCode(_script, opts, _attrs, url)
return ''
})
body = body.replace(STYLE, included('css') ? function (_, _attrs, _style) {
var scoped = _attrs && /\sscoped(\s|=|$)/i.test(_attrs),
csstype = getType(_attrs) || opts.style
styles += (styles ? ' ' : '') +
compileCSS(_style, tagName, csstype, scoped, getParserOptions(_attrs), url)
return ''
} : '')
var blocks = splitBlocks(body.replace(TRIM_TRAIL, ''))
body = body.replace(SCRIPT, included('js') ? function (_, _attrs, _script) {
jscode += (jscode ? '\n' : '') + getCode(_script, opts, _attrs, url)
return ''
} : '')
var blocks = splitBlocks(body.replace(TRIM_TRAIL, ''))
if (included('html')) {
body = blocks[0]
if (body)
html = compileHTML(body, opts, pcex, 1)
}
if (included('js')) {
body = blocks[1]
if (/\S/.test(body))
jscode += (jscode ? '\n' : '') + compileJS(body, opts)
jscode += (jscode ? '\n' : '') + compileJS(body, opts, null, null, url)
}
}
}
jscode = /\S/.test(jscode) ? jscode.replace(/\n{3,}/g, '\n\n') : ''
jscode = /\S/.test(jscode) ? jscode.replace(/\n{3,}/g, '\n\n') : ''
if (opts.entities) {
parts.push({
tagName: tagName,
html: html,
css: styles,
attribs: attribs,
js: jscode
})
return ''
}
if (opts.entities) {
parts.push({
tagName: tagName,
html: html,
css: styles,
attribs: attribs,
js: jscode
})
return ''
}
return mktag(tagName, html, styles, attribs, jscode, pcex)
})
return mktag(tagName, html, styles, attribs, jscode, pcex)
})
return opts.entities ? parts : src
}
if (opts.entities) return parts
return {
compile: compile,
html: compileHTML,
style: compileCSS,
js: compileJS,
parsers: parsers
if (url && opts.debug) {
/* istanbul ignore if */
if (path.isAbsolute(url)) url = path.relative('.', url)
src = '//src: ' + url.replace(/\\/g, '/') + '\n' + src
}
})
return src
}
module.exports = {
compile: compile,
html: compileHTML,
style: compileCSS,
js: compileJS,
parsers: parsers,
version: 'v2.3.16-beta'
}
/**
* Compiler for riot custom tags
* @version v2.3.13
*/
/**
* @module parsers

@@ -13,3 +8,4 @@ */

function _try(name, req) { //eslint-disable-line no-redeclare
function _try(name, req) { //eslint-disable-line complexity
var parser

@@ -27,3 +23,8 @@ switch (name) {

}
return _mods[name] = window[req]
parser = window[req]
if (!parser)
throw new Error(req + ' parser not found.')
return parser
}

@@ -36,4 +37,8 @@

var _html = {
jade: function (html, opts) {
return _req('jade').render(html, extend({pretty: true, doctype: 'html'}, opts))
jade: function (html, opts, url) {
return _req('jade').render(html, extend({
pretty: true,
filename: url,
doctype: 'html'
}, opts))
}

@@ -43,3 +48,17 @@ }

var _css = {
stylus: function (tag, css, opts) {
less: function(tag, css, opts, url) {
var less = _req('less'),
ret
less.render(css, extend({
sync: true,
compress: true
}, opts), function (err, result) {
// istanbul ignore next
if (err) throw err
ret = result.css
})
return ret
},
stylus: function (tag, css, opts, url) {
var

@@ -54,12 +73,12 @@ stylus = _req('stylus'), nib = _req('nib')

var _js = {
none: function (js, opts) {
none: function (js, opts, url) {
return js
},
livescript: function (js, opts) {
livescript: function (js, opts, url) {
return _req('livescript').compile(js, extend({bare: true, header: false}, opts))
},
typescript: function (js, opts) {
typescript: function (js, opts, url) {
return _req('typescript')(js, opts).replace(/\r\n?/g, '\n')
},
es6: function (js, opts) {
es6: function (js, opts, url) {
return _req('es6').transform(js, extend({

@@ -69,11 +88,10 @@ blacklist: ['useStrict', 'strict', 'react'], sourceMaps: false, comments: false

},
babel: function (js, opts) {
js = 'function __parser_babel_wrapper__(){' + js + '}'
babel: function (js, opts, url) {
return _req('babel').transform(js,
extend({
presets: ['es2015']
filename: url
}, opts)
).code.replace(/["']use strict["'];[\r\n]+/, '').slice(38, -2)
).code
},
coffee: function (js, opts) {
coffee: function (js, opts, url) {
return _req('coffee').compile(js, extend({bare: true}, opts))

@@ -93,7 +111,11 @@ }

/**
* @module compiler
* Compiler for riot custom tags
* @version v2.3.16-beta
*/
var compile = (function () {
var brackets = riot.util.brackets
// istanbul ignore next
if (!brackets.version) {
throw new Error('This compiler version requires riot-tmpl v2.3.16 or above')
}

@@ -106,3 +128,3 @@ function _regEx(str, opt) { return new RegExp(str, opt) }

'^(?:disabled|checked|readonly|required|allowfullscreen|auto(?:focus|play)|' +
'compact|controls|default|formnovalidate|hidden|inert|ismap|itemscope|loop|' +
'compact|controls|default|formnovalidate|hidden|ismap|itemscope|loop|' +
'multiple|muted|no(?:resize|shade|validate|wrap)?|open|reversed|seamless|' +

@@ -115,8 +137,6 @@ 'selected|sortable|truespeed|typemustmatch)$'),

HTML_ATTR = /\s*([-\w:\.\xA0-\xFF]+)\s*(?:=\s*('[^']+'|"[^"]+"|\S+))?/g,
HTML_ATTR = /\s*([-\w:\xA0-\xFF]+)\s*(?:=\s*('[^']+'|"[^"]+"|\S+))?/g,
TRIM_TRAIL = /[ \t]+$/gm,
TRIM_TRAIL = /[ \t]+$/gm
_bp = null
function q(s) {

@@ -131,3 +151,3 @@ return "'" + (s ? s

c = ', ',
s = '}' + (pcex.length ? ', ' + q(_bp[8]) : '') + ');'
s = '}' + (pcex.length ? ', ' + q(pcex._bp[8]) : '') + ');'

@@ -150,3 +170,3 @@ if (js && js.slice(-1) !== '\n') s = '\n' + s

function parseAttrs(str) {
function parseAttrs(str, pcex) {
var

@@ -156,5 +176,9 @@ list = [],

k, v,
_bp = pcex._bp,
DQ = '"'
HTML_ATTR.lastIndex = 0
str = str.replace(/\s+/g, ' ')
while (match = HTML_ATTR.exec(str)) {

@@ -193,2 +217,3 @@

function splitHtml(html, opts, pcex) {
var _bp = pcex._bp

@@ -198,3 +223,3 @@ if (html && _bp[4].test(html)) {

jsfn = opts.expr && (opts.parser || opts.type) ? compileJS : 0,
list = brackets.split(html),
list = brackets.split(html, 0, _bp),
expr

@@ -207,4 +232,6 @@

else if (jsfn) {
expr = jsfn(expr, opts)
if (/;\s*$/.test(expr)) expr = expr.slice(0, expr.search(/;\s*$/))
var israw = expr[0] === '='
expr = jsfn(israw ? expr.slice(1) : expr, opts).trim()
if (expr.slice(-1) === ';') expr = expr.slice(0, -1)
if (israw) expr = '=' + expr
}

@@ -222,3 +249,11 @@ list[i] = '\u0001' + (pcex.push(expr.replace(/[\r\n]+/g, ' ').trim()) - 1) + _bp[1]

.replace(/\u0001(\d+)/g, function (_, d) {
return _bp[0] + pcex[d].replace(/"/g, '&quot;')
var expr = pcex[d]
if (expr[0] === '=') {
expr = expr.replace(brackets.R_STRINGS, function (qs) {
return qs
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
})
}
return pcex._bp[0] + expr
})

@@ -231,12 +266,15 @@ }

HTML_COMMENT = /<!--(?!>)[\S\s]*?-->/g,
HTML_TAGS = /<([-\w]+)\s*([^"'\/>]*(?:(?:"[^"]*"|'[^']*'|\/[^>])[^'"\/>]*)*)(\/?)>/g
HTML_TAGS = /<([-\w]+)\s*([^"'\/>]*(?:(?:"[^"]*"|'[^']*'|\/[^>])[^'"\/>]*)*)(\/?)>/g,
PRE_TAG = _regEx(
/<pre(?:\s+[^'">]+(?:(?:@Q)[^'">]*)*|\s*)?>([\S\s]*?)<\/pre\s*>/
.source.replace('@Q', brackets.R_STRINGS.source), 'gi')
function compileHTML(html, opts, pcex, intc) {
function compileHTML(html, opts, pcex ) {
if (!intc) {
_bp = brackets.array(opts.brackets)
var intf = (pcex || (pcex = []))._intflag
if (!intf)
html = html.replace(/\r\n?/g, '\n').replace(HTML_COMMENT, '').replace(TRIM_TRAIL, '')
}
if (!pcex) pcex = []
if (!pcex._bp) pcex._bp = brackets.array(opts.brackets)
html = splitHtml(html, opts, pcex)

@@ -249,3 +287,3 @@ .replace(HTML_TAGS, function (_, name, attr, ends) {

if (attr) name += ' ' + parseAttrs(attr)
if (attr) name += ' ' + parseAttrs(attr, pcex)

@@ -256,9 +294,12 @@ return '<' + name + ends + '>'

if (!opts.whitespace) {
var p = [],
pre = /<pre(?:\s+[^'">]+(?:(?:"[^"]*"|'[^']*')[^'">]*)*|\s*)>[\s\S]*<\/pre\s*>/gi
html = html.replace(pre, function (q) {
return '\u0002' + (p.push(q) - 1) + '~' }).trim().replace(/\s+/g, ' ')
if (p.length)
html = html.replace(/\u0002(\d+)~/g, function (q, n) { return p[n] })
if (/<pre[\s>]/.test(html)) {
var p = []
html = html.replace(PRE_TAG, function (q)
{ return p.push(q) && '\u0002' }).trim().replace(/\s+/g, ' ')
// istanbul ignore else
if (p.length)
html = html.replace(/\u0002/g, function (_) { return p.shift() })
}
else
html = html.trim().replace(/\s+/g, ' ')
}

@@ -272,7 +313,3 @@

var
JS_RMCOMMS = _regEx(
'(' + brackets.S_QBLOCKS + ')|' + brackets.R_MLCOMMS.source + '|//[^\r\n]*',
'g'),
JS_RMCOMMS = _regEx('(' + brackets.S_QBLOCKS + ')|' + brackets.R_MLCOMMS.source + '|//[^\r\n]*', 'g'),
JS_ES6SIGN = /^([ \t]*)([$_A-Za-z][$\w]*)\s*(\([^()]*\)\s*{)/m

@@ -320,3 +357,3 @@

function compileJS(js, opts, type, parserOpts) {
function compileJS(js, opts, type, parserOpts, url) {
if (!js) return ''

@@ -329,3 +366,3 @@ if (!type) type = opts.type

return parser(js, parserOpts).replace(TRIM_TRAIL, '')
return parser(js, parserOpts, url).replace(TRIM_TRAIL, '')
}

@@ -398,3 +435,3 @@

if (str)
return /^['"]/.test(str) ? str.slice(1, -1) : str
return (/^['"]/).test(str) ? str.slice(1, -1) : str
}

@@ -404,6 +441,5 @@ return ''

// get the parser options from the options attribute
function getParserOptions(attrs) {
var opts = getAttr(attrs, 'options')
// convert the string into a valid js object
if (opts) opts = JSON.parse(opts)

@@ -413,4 +449,2 @@ return opts

// Runs the custom or default parser on the received JavaScript code.
// The CLI version can read code from the file system (experimental)
function getCode(code, opts, attrs, url) {

@@ -420,19 +454,5 @@ var type = getType(attrs),

//#if READ_JS_SRC
var src = getAttr(attrs, 'src')
if (src && url) {
var
charset = getAttr(attrs, 'charset'),
file = path.resolve(path.dirname(url), src)
code = require('fs').readFileSync(file, {encoding: charset || 'utf8'})
}
//#endif
return compileJS(code, opts, type, parserOpts)
return compileJS(code, opts, type, parserOpts, url)
}
// Matches HTML tag ending a line. This regex still can be fooled by code as:
// ```js
// x <y && y >
// z
// ```
var END_TAGS = /\/>\n|^<(?:\/[\w\-]+\s*|[\w\-]+(?:\s+(?:[-\w:\xA0-\xFF][\S\s]*?)?)?)>\n/

@@ -444,6 +464,5 @@

/* istanbul ignore next: this if() can't be true, but just in case... */
if (str[str.length - 1] === '>')
return [str, '']
if (str[str.length - 1] === '>') return [str, '']
k = str.lastIndexOf('<') // first probable open tag
k = str.lastIndexOf('<')
while (~k) {

@@ -456,8 +475,6 @@ if (m = str.slice(k).match(END_TAGS)) {

}
return ['', str]
}
// Runs the external HTML parser for the entire tag file
function compileTemplate(lang, html, opts) {
function compileTemplate(html, url, lang, opts) {
var parser = parsers.html[lang]

@@ -468,15 +485,9 @@

return parser(html, opts)
return parser(html, opts, url)
}
/*
CUST_TAG regex don't allow unquoted expressions containing the `>` operator.
STYLE and SCRIPT disallows the operator `>` at all.
The beta.4 CUST_TAG regex is fast, with RegexBuddy I get 76 steps and 14 backtracks on
the test/specs/fixtures/treeview.tag :) but fails with nested tags of the same name :(
With a greedy * operator, we have ~500 and 200bt, it is acceptable. So let's fix this.
*/
var
CUST_TAG = /^([ \t]*)<([-\w]+)(?:\s+([^'"\/>]+(?:(?:"[^"]*"|'[^']*'|\/[^>])[^'"\/>]*)*)|\s*)?(?:\/>|>[ \t]*\n?([\s\S]*)^\1<\/\2\s*>|>(.*)<\/\2\s*>)/gim,
CUST_TAG = _regEx(
/^([ \t]*)<([-\w]+)(?:\s+([^'"\/>]+(?:(?:@Q|\/[^>])[^'"\/>]*)*)|\s*)?(?:\/>|>[ \t]*\n?([\S\s]*)^\1<\/\2\s*>|>(.*)<\/\2\s*>)/
.source.replace('@Q', brackets.R_STRINGS.source), 'gim'),
STYLE = /<style(\s+[^>]*)?>\n?([^<]*(?:<(?!\/style\s*>)[^<]*)*)<\/style\s*>/gi,

@@ -486,14 +497,17 @@ SCRIPT = _regEx(STYLE.source.replace(/tyle/g, 'cript'), 'gi')

function compile(src, opts, url) {
var label, parts = []
var
parts = [],
exclude
if (!opts) opts = {}
_bp = brackets.array(opts.brackets)
exclude = opts.exclude || false
function included(s) { return !(exclude && ~exclude.indexOf(s)) }
var _bp = brackets.array(opts.brackets)
if (opts.template)
src = compileTemplate(opts.template, src, opts.templateOptions)
src = compileTemplate(src, url, opts.template, opts.templateOptions)
label = url ? '//src: ' + url + '\n' : ''
src = label + src
src = src
.replace(/\r\n?/g, '\n')

@@ -508,5 +522,9 @@ .replace(CUST_TAG, function (_, indent, tagName, attribs, body, body2) {

pcex._bp = _bp
pcex._intflag = 1
tagName = tagName.toLowerCase()
attribs = !attribs ? '' : restoreExpr(parseAttrs(splitHtml(attribs, opts, pcex)), pcex)
attribs = attribs && included('attribs') ?
restoreExpr(parseAttrs(splitHtml(attribs, opts, pcex), pcex), pcex) : ''

@@ -517,29 +535,35 @@ if (body2) body = body2

if (body2)
html = compileHTML(body2, opts, pcex, 1)
if (body2) {
/* istanbul ignore next */
html = included('html') ? compileHTML(body2, opts, pcex, 1, url) : ''
}
else {
body = body.replace(_regEx('^' + indent, 'gm'), '')
body = body.replace(STYLE, function (_, _attrs, _style) {
body = body.replace(STYLE, included('css') ? function (_, _attrs, _style) {
var scoped = _attrs && /\sscoped(\s|=|$)/i.test(_attrs),
csstype = getType(_attrs) || opts.style
styles += (styles ? ' ' : '') +
compileCSS(_style, tagName, csstype, scoped, getParserOptions(_attrs))
compileCSS(_style, tagName, csstype, scoped, getParserOptions(_attrs), url)
return ''
})
} : '')
body = body.replace(SCRIPT, function (_, _attrs, _script) {
jscode += (jscode ? '\n' : '') + getCode(_script, opts, _attrs)
body = body.replace(SCRIPT, included('js') ? function (_, _attrs, _script) {
jscode += (jscode ? '\n' : '') + getCode(_script, opts, _attrs, url)
return ''
})
} : '')
var blocks = splitBlocks(body.replace(TRIM_TRAIL, ''))
body = blocks[0]
if (body)
html = compileHTML(body, opts, pcex, 1)
if (included('html')) {
body = blocks[0]
if (body)
html = compileHTML(body, opts, pcex, 1)
}
body = blocks[1]
if (/\S/.test(body))
jscode += (jscode ? '\n' : '') + compileJS(body, opts)
if (included('js')) {
body = blocks[1]
if (/\S/.test(body))
jscode += (jscode ? '\n' : '') + compileJS(body, opts, null, null, url)
}
}

@@ -564,8 +588,16 @@ }

return opts.entities ? parts : src
if (opts.entities) return parts
return src
}
riot.util.compiler = {
compile: compile,
html: compileHTML,
style: compileCSS,
js: compileJS,
version: 'v2.3.16-beta'
}
return compile
})()

@@ -30,5 +30,4 @@ Boolean attributes in riot 2.3

There's no official documentation with a full list of boolean attributes, but we collect
that information from different sources.
Currently riot v2.3.x recognizes these boolean attributes:
There's no official list with all the boolean attributes, but we collect that information from different sources.
Currently riot v2.3.x recognizes these:

@@ -53,6 +52,5 @@ * allowfullscreen - `<iframe>` - WHATWG HTML Living Standard, not in W3C HTML5

* controls - `<audio>`/`<video>`
* default - `<track>`/`<menuitem>` - menuitem is not yet supported by browsers
* default - `<track>`/`<menuitem>` (menuitem is not yet supported by browsers)
* formnovalidate
* hidden
* inert - deprecated
* itemscope - for html5 Microdata

@@ -66,3 +64,3 @@ * loop - `<audio>`/`<video>`

* sortable - html 5.1
* typemustmatch
* typemustmatch - `<object>`

@@ -72,4 +70,2 @@ **WARNING:** Please don't use expressions in the `loop` attribute within `<img>` tags.

The `inert` attribute [was dropped](https://html5.org/r/8536) from the html5 spec in 2014.
Tested with [the w3c Validator](https://validator.w3.org/nu)

@@ -87,2 +83,3 @@

* draggable - not boolean, this is an enumerated attribute: true, false, auto
* inert - this proposed html5 attribute was [dropped](https://html5.org/r/8536) from the spec
* enabled - not in the HTML spec

@@ -89,0 +86,0 @@ * indeterminate - boolean attr, but can't be set with markup

@@ -5,6 +5,6 @@ # Compiler Guide (complement, WIP)

In v2.3.12 the compiler handles a more consistent and flexible indentation in both inline and external tag definitions.
From v2.3.13, the compiler handles a more consistent and flexible indentation in both inline and external tag definitions.
The opening tag must begin a line. You can use any tabs or spaces you want. The compiler uses this to find the closing tag and unindent the content, so the closing tag must have _exactly_ the same indentation of the opening tag.
HTML comments and trailing whitespace are removed from the entire tag content (JavaScript comments are removed in the JavaScript block only, so you can not use comment in expressions).
HTML comments and trailing whitespace are removed from the entire tag content, JavaScript comments are removed from the JavaScript block only. You should not use comments in expressions.

@@ -30,13 +30,113 @@ Example:

```
note the `<pre>` content, this is unindented by 2 too.
## Backslashes and Whitespace
Note the `<pre>` content, this is unindented by 2 too.
From the perspective of the riot compiler and `tmpl`, backslashes in the template are characters with no special meaning. The compiler preserves them in the HTML and expressions, with one exception: backslashes used to escape riot brackets are temporarily removed when the expression is passed to a parser, and finally removed at runtime, before evaluating the expression.
In quoted strings and regexes inside expressions, all whitespace are preserved.
In the html, including quoted text, newlines are converted to spaces and compacted, except if you pass the `whitespace` option to the compiler. With this option
### One-line tags
No matter which options are used, newlines are normalized to `\n` and trailing spaces are removed.
Riot can handle one-line tag definitions like this:
```html
<oneline attrib="x"/>
```
or this:
```html
<oneline attrib="x"><p><!-- --></p></oneline>
```
As multiline tags, these can be indentent by any tabs or spaces, but can not contain `style`/`script` tags nor untagged JavaScript code, only html markup.
### Untagged html content
From the Riot Guide:
> Without the `<script>` tag the JavaScript starts where the last HTML tag ends.
If there's no HTML tags within the root tag, riot assumes that the content is JavaScript, so the following does not work:
```html
<my-tag>
I'm html?
</my-tag>
```
This may seem counterintuitive, but maintains backward compatibility with the behavior of previous versions.
See [The untagged JS block](the-untagged-js-block) for details.
### Whitespace
In the html, including quoted text, newlines are converted to spaces and compacted (successive empty lines are merged into one), except if you pass the `whitespace` option to the compiler, in which case only takes place normalization to Unix-style line endings.
The following table summarizes this behavior with [different options](#compilation-options):
| example | generates | options
| ------- | --------- | -------
| `<p>\r<p>\n<p> </p>` | `<p> </p> <p> </p>` | (none)
| `<p>\r<p>\n<p> </p>` | `<p>\\n</p>\\n<p> </p>` | `whitespace:true`
| `<p>\r<p>\n<p> </p>` | `<p></p><p></p>` | `compact:true`
| `<p>\r<p>\n<p> </p>` | `<p>\\n</p>\\n<p></p>` | `compact:true, whitespace:true`
**In other parts**
Tag attributes, including these within nested tags, are normalized. This is:
* the attribute name is converted to lowercase
* newlines are converted to compacted spaces
* spacing between name and value is removed
* the value is enclosed in double quotes.
Content of `style` blocks and expressions are trimmed and newlines converted to compacted spaces.
This example shows the behavior with the default options on different parts of a tag:
```html
<my-tag
style='
top:0;
left:0' expr={
{ foo:"bar" }
}>
<style>
p {
display: none;
}
</style>
<p/>
click(e)
{}
</my-tag>
```
will generate this:
```js
riot.tag2('my-tag', '<p></p>', 'p { display: none; }', 'style=" top:0; left:0" expr="{{ foo:"bar" }}"', function(opts) {
this.click = function(e)
{}.bind(this)
}, '{ }');
```
**NOTE:** No matter which options you use, newlines are normalized to `\n` and comments and trailing spaces are removed before the parsing begins.
### Brackets and backslashes
From the perspective of the riot compiler, backslashes in the template are characters with no special meaning.
The compiler preserves them with one exception: backslashes inside expressions used to escape riot brackets are removed. This occurs just before the expression is passed to any JS parser.
Actually, with correct JavaScript, the compiler is a bit smarter and does not need escaped brackets _within_ expressions.
However, it is needed for literal opening brackets out of expressions, since there is no way to differentiate from riot brackets.
Example:
```html
<my-tag non-expr="\{ empty:\{} }" expr="{ empty:{} }"></my-tag>
```
In the first, non-expr attribute, opening brackets must be escaped.
The generated code is:
```js
// This JS comment is at column 0, out of the tag
riot.tag2('my-tag', '', '', 'non-expr="\\{ empty:\\{} }" expr="{empty:{}}"', function(opts) {
click(e) {}
}); // the closing tag is indented by 2 spaces, this comment must be JS
```
at runtime, the backslashes in `non-expr` will be removed. If you need output a literal backslash in front of brackets, you must use `\\{` to generate `\\\\{`.
## Compilation options

@@ -50,17 +150,49 @@

| expr | html | boolean | Run expressions through the parser defined with the `type` option
| compact | html | boolean | Remove spaces between tags (minify `</p> <p>` to `</p><p>`)
| compact | html | boolean | Remove spaces between tags (minify `<p> </p> <p> </p>` to `<p></p><p></p>`)
| whitespace | html | boolean | Preserve newlines and tabs. newlines are normalized anyway
| template | html | string | HTML pre-processor. Built-in support for: jade
| type | js | string | JavaScript pre-processor. Built-in support for: es6, babel, coffeescript, typescript, livescript, none
| style | css | string | CSS pre-processor. Built-in support for: jade
| style | css | string | CSS pre-processor. Built-in support for: sass, scss, less
| entities | compile | boolean | Split the tag in its raw parts.
| exclude | compile | array | This is an array of strings with part names to exclude. These names are the same as those generated by the `entities` option: html, css, attribs, js. Ej. `{exclude: ['js']}`
### The `entities` option
This option, new in v2.3.13, causes the `compile` function return an array of objects with the parts of the tags.
Each object contains five properties: tagName, html, attribs, css, and js. Propertie values for non-existent parts are empty strings.
Example:
```html
<script type="riot/tag" id="test1">
// two custom tags
<tag1 id="id1"><div/></tag1>
<tag2>
<style>#id1 {top:0}</style>
<p/>
click(e) {
}
</tag2>
</script>
<script>
var arr = compiler.compile(document.getElementById('test1').innerHTML, {entities: true})
</script>
```
will set `arr` to be:
```js
[
{tagName: 'tag1', html: '<div></div>', css: '', attribs: 'id="id1"', js: ''},
{tagName: 'tag2', html: '<p></p>', css: '#id1 {top:0}', attribs: '', js: ' this.click = function(e) {\n }.bind(this);' }
]
```
Be aware that the `html` and `js` properties can contain raw line endings --i.e. unescaped `\r` and/or `\n`.
## Parser options
In addition to the `type` attribute, script and style tags can include additional configuration at tag level through the `options` attribute.
In addition to the `type` attribute, `script` and `style` tags can include additional configuration at tag level through the `options` attribute.
**NOTE:** This attribute is not an expression, it is a [JSON](http://json.org/) object and must comply with the [JSON specs](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf), i.e. suitable for `JSON.parse`.
This attribute is a [JSON](http://json.org/) object that specifies options for the JS or CSS parser.
The `options` attribute specify options for the JS or CSS parser.
Example:

@@ -80,21 +212,26 @@ ```html

**NOTE:** The `options` attribute must comply with the [JSON specs](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf), i.e. with double-quoted property names and strings, in a format suitable for `JSON.parse`
## The untagged JS block
Where the html ends? or where should I put my JS?
The first action taken by the compiler is send the received source to any htm parser. After that,
it normalizes line endings to `\n` and removes _html_ comments.
Once prepared the source, searches for tags, separate the parts (closing/opening tag, root
attributes, and content). In the content, search the `<style>` blocks, removes and sends their
content to the CSS parser. Next, it does the same for the `<script>` tags.
## The JavaScript
Where the html ends? or where should I put my JavaScript?
### The untagged JS block
The first action taken by the compiler is send the received source to any html parser.
After that, it normalizes line endings to `\n` and removes _html_ comments.
Once prepared the source, searches the tags, separate its parts (closing/opening tag, root
attributes, and content). In the content, one by one, removes the `style` blocks and sends its
content to the CSS parser. Next, it does the same for the `script` tags.
This is done in the entire content.
In the remaining source, look for the last html tag (ending a line). If found, this marks the
end of the html and the beginning of the JavaScript code. If not found, all remaining is
considered JavaScript.
In the remaining content, looks for the last html tag which ends a line.
If found, this closing tag signals the end of the html markup and the beginning of the JavaScript code.
If not found, all remaining is considered JavaScript.
So, you can put html comments, `style`, and `script` blocks anywhere inside the tag.
The only restriction is that the untagged JavaScript code must finish the content.
So, you can put html comments, `style`, and `script` blocks, anywhere inside the tag.
The only restriction is that the untagged JavaScript code must finish the content and you can't use JavaScript comments out of this block.
## Multiple JavaScript blocks
### Multiple JavaScript blocks

@@ -104,3 +241,3 @@ Each JavaScript block in the tag can have different `type` attributes.

## Loading JavaScript from the file system
### Loading JavaScript from the file system (v2.3.13)

@@ -111,4 +248,14 @@ The `src` attribute of the `script` tags inside a riot tag, allows load source files from the file system.

For this feature to work, you need to pass a third parameter to the `compile` function: the name of the file being compiled.
Example:
```js
var compile = require('riot-compile'),
fs = require('fs')
var source = fs.readFileSync(full_filename, 'utf8')
var result = compiler.compile(source, options, full_filename)
```
So, if we have a mytag.tag and a js/data.js file...
```js
// ./js/data.js file

@@ -125,3 +272,3 @@ this.title = "my title"

is equivalent to
the result is equivalent to
```html

@@ -135,1 +282,9 @@ <my-tag>

```
## Style
(WIP)
### Scoped style
(WIP)

@@ -151,2 +151,3 @@ # Compiler

- `sass`
- `scss`
- `stylus`

@@ -153,0 +154,0 @@

@@ -1,630 +0,651 @@

/**
* @module compiler
*/
//#if RIOT
var compile = (function () {
var brackets = riot.util.brackets
//#else
//#define READ_JS_SRC
// istanbul ignore next
if (!brackets.version) {
throw new Error('This compiler version requires riot-tmpl v2.3.16 or above')
}
var brackets = _tmpl.brackets //eslint-disable-line no-redeclare
//#endif
function _regEx(str, opt) { return new RegExp(str, opt) }
function _regEx(str, opt) { return new RegExp(str, opt) }
// Looks like, in [jsperf tests](http://jsperf.com/riot-regexp-test-vs-array-indexof)
// RegExp is faster in most browsers, except for very shorty arrays.
var
// Boolean attributes, prefixed with `__` in the riot tag definition.
// See ../doc/attributes.md
//
BOOL_ATTRS = _regEx(
'^(?:disabled|checked|readonly|required|allowfullscreen|auto(?:focus|play)|' +
'compact|controls|default|formnovalidate|hidden|ismap|itemscope|loop|' +
'multiple|muted|no(?:resize|shade|validate|wrap)?|open|reversed|seamless|' +
'selected|sortable|truespeed|typemustmatch)$'),
// Looks like, in [jsperf tests](http://jsperf.com/riot-regexp-test-vs-array-indexof)
// RegExp is faster in most browsers, except for very shorty arrays.
var
// Boolean attributes, prefixed with `__` in the riot tag definition.
// See ../doc/attributes.md
//
BOOL_ATTRS = _regEx(
'^(?:disabled|checked|readonly|required|allowfullscreen|auto(?:focus|play)|' +
'compact|controls|default|formnovalidate|hidden|inert|ismap|itemscope|loop|' +
'multiple|muted|no(?:resize|shade|validate|wrap)?|open|reversed|seamless|' +
'selected|sortable|truespeed|typemustmatch)$'),
// The following attributes give error when parsed on browser with `{ exrp_value }`
// See ../doc/attributes.md
RIOT_ATTRS = ['style', 'src', 'd'],
// The following attributes give error when parsed on browser with `{ exrp_value }`
// See ../doc/attributes.md
RIOT_ATTRS = ['style', 'src', 'd'],
// HTML5 void elements that cannot be auto-closed.
// See: http://www.w3.org/TR/html-markup/syntax.html#syntax-elements
// http://www.w3.org/TR/html5/syntax.html#void-elements
VOID_TAGS = /^(?:input|img|br|wbr|hr|area|base|col|embed|keygen|link|meta|param|source|track)$/,
// HTML5 void elements that cannot be auto-closed.
// See: http://www.w3.org/TR/html-markup/syntax.html#syntax-elements
// http://www.w3.org/TR/html5/syntax.html#void-elements
VOID_TAGS = /^(?:input|img|br|wbr|hr|area|base|col|embed|keygen|link|meta|param|source|track)$/,
// Matches attributes. Names can contain almost all iso-8859-1 character set.
HTML_ATTR = /\s*([-\w:\xA0-\xFF]+)\s*(?:=\s*('[^']+'|"[^"]+"|\S+))?/g,
// Matches attributes. Names can contain almost all iso-8859-1 character set.
HTML_ATTR = /\s*([-\w:\.\xA0-\xFF]+)\s*(?:=\s*('[^']+'|"[^"]+"|\S+))?/g,
TRIM_TRAIL = /[ \t]+$/gm
TRIM_TRAIL = /[ \t]+$/gm,
//#if NODE
var path = require('path')
//#endif
_bp = null
//#set $_RIX_TEST = 4
//#set $_RIX_ESC = 5
//#set $_RIX_OPEN = 6
//#set $_RIX_CLOSE = 7
//#set $_RIX_PAIR = 8
//#ifndef $_RIX_TEST
var
$_RIX_TEST = 4, // DONT'T FORGET SYNC THE #set BLOCK!!!
$_RIX_ESC = 5,
$_RIX_OPEN = 6,
$_RIX_CLOSE = 7,
$_RIX_PAIR = 8
//#endif
//#if NODE
var path = require('path')
//#endif
// Escape backslashes and inner single quotes, and enclose s in single quotes
function q(s) {
return "'" + (s ? s
.replace(/\\/g, '\\\\').replace(/'/g, "\\'").replace(/\n/g, '\\n').replace(/\r/g, '\\r') :
'') + "'"
}
//#set $_RIX_TEST = 4
//#set $_RIX_ESC = 5
//#set $_RIX_OPEN = 6
//#set $_RIX_CLOSE = 7
//#set $_RIX_PAIR = 8
//#ifndef $_RIX_TEST
// Generates the `riot.tag2` call with the processed parts.
function mktag(name, html, css, attrs, js, pcex) {
var
$_RIX_TEST = 4, // DONT'T FORGET SYNC THE #set BLOCK!!!
$_RIX_ESC = 5,
$_RIX_OPEN = 6,
$_RIX_CLOSE = 7,
$_RIX_PAIR = 8
//#endif
c = ', ',
s = '}' + (pcex.length ? ', ' + q(pcex._bp[$_RIX_PAIR]) : '') + ');'
// Escape backslashes and inner single quotes, and enclose s in single quotes
function q(s) {
return "'" + (s ? s
.replace(/\\/g, '\\\\').replace(/'/g, "\\'").replace(/\n/g, '\\n').replace(/\r/g, '\\r') :
'') + "'"
// give more consistency to the output
if (js && js.slice(-1) !== '\n') s = '\n' + s
return 'riot.tag2(\'' + name + "'" + c + q(html) + c + q(css) + c + q(attrs) +
', function(opts) {\n' + js + s
}
/**
* Merge two javascript object extending the properties of the first one with
* the second
*
* @param {object} obj - source object
* @param {object} props - extra properties
* @returns {object} source object containing the new properties
*/
function extend(obj, props) {
for (var prop in props) {
/* istanbul ignore next */
if (props.hasOwnProperty(prop)) {
obj[prop] = props[prop]
}
}
return obj
}
// Generates the `riot.tag2` call with the processed parts.
function mktag(name, html, css, attrs, js, pcex) {
var
c = ', ',
s = '}' + (pcex.length ? ', ' + q(_bp[$_RIX_PAIR]) : '') + ');'
/**
* Parses and format attributes.
*
* @param {string} str - Attributes, with expressions replaced by their hash
* @param {Array} pcex - Has a _bp property with info about brackets
* @returns {string} Formated attributes
*/
function parseAttrs(str, pcex) {
var
list = [],
match,
k, v,
_bp = pcex._bp,
DQ = '"'
// give more consistency to the output
if (js && js.slice(-1) !== '\n') s = '\n' + s
HTML_ATTR.lastIndex = 0
return 'riot.tag2(\'' + name + "'" + c + q(html) + c + q(css) + c + q(attrs) +
', function(opts) {\n' + js + s
}
str = str.replace(/\s+/g, ' ')
/**
* Merge two javascript object extending the properties of the first one with
* the second
* @param {object} obj - source object
* @param {object} props - extra properties
* @returns {object} source object containing the new properties
*/
function extend(obj, props) {
for (var prop in props) {
/* istanbul ignore next */
if (props.hasOwnProperty(prop)) {
obj[prop] = props[prop]
}
}
return obj
}
while (match = HTML_ATTR.exec(str)) {
/**
* Parses and format attributes.
*
* @param {string} str - Attributes, with expressions replaced by their hash
* @returns {string} Formated attributes
*/
function parseAttrs(str) {
var
list = [],
match,
k, v,
DQ = '"'
HTML_ATTR.lastIndex = 0
// all attribute names are converted to lower case
k = match[1].toLowerCase()
v = match[2]
while (match = HTML_ATTR.exec(str)) {
if (!v) {
list.push(k) // boolean attribute without explicit value
}
else {
// all attribute names are converted to lower case
k = match[1].toLowerCase()
v = match[2]
// attribute values must be enclosed in double quotes
if (v[0] !== DQ)
v = DQ + (v[0] === "'" ? v.slice(1, -1) : v) + DQ
if (!v) {
list.push(k) // boolean attribute without explicit value
if (k === 'type' && v.toLowerCase() === '"number"') {
v = DQ + _bp[0] + "'number'" + _bp[1] + DQ // fix #827 by @rsbondi
}
else {
// attribute values must be enclosed in double quotes
if (v[0] !== DQ)
v = DQ + (v[0] === "'" ? v.slice(1, -1) : v) + DQ
if (k === 'type' && v.toLowerCase() === '"number"') {
v = DQ + _bp[0] + "'number'" + _bp[1] + DQ // fix #827 by @rsbondi
else if (/\u0001\d/.test(v)) {
// renames special attributes with expressiones in their value.
if (BOOL_ATTRS.test(k)) {
k = '__' + k
}
else if (/\u0001\d/.test(v)) {
// renames special attributes with expressiones in their value.
if (BOOL_ATTRS.test(k)) {
k = '__' + k
}
else if (~RIOT_ATTRS.indexOf(k)) {
k = 'riot-' + k
}
else if (~RIOT_ATTRS.indexOf(k)) {
k = 'riot-' + k
}
}
// join the key-value pair, with no spaces between the parts
list.push(k + '=' + v)
}
// join the key-value pair, with no spaces between the parts
list.push(k + '=' + v)
}
return list.join(' ') // returns the attribute list
}
return list.join(' ') // returns the attribute list
}
// Replaces expressions in the HTML with a marker, and runs expressions
// through the parser, except those beginning with `{^`.
function splitHtml(html, opts, pcex) {
// Replaces expressions in the HTML with a marker, and runs expressions
// through the parser, except those beginning with `{^`.
function splitHtml(html, opts, pcex) {
var _bp = pcex._bp
// `brackets.split` is a heavy function, so don't call it if not necessary
if (html && _bp[$_RIX_TEST].test(html)) {
var
jsfn = opts.expr && (opts.parser || opts.type) ? compileJS : 0,
list = brackets.split(html),
expr
// `brackets.split` is a heavy function, so don't call it if not necessary
if (html && _bp[$_RIX_TEST].test(html)) {
var
jsfn = opts.expr && (opts.parser || opts.type) ? compileJS : 0,
list = brackets.split(html, 0, _bp),
expr
for (var i = 1; i < list.length; i += 2) {
expr = list[i]
if (expr[0] === '^')
expr = expr.slice(1)
else if (jsfn) {
expr = jsfn(expr, opts)
if (/;\s*$/.test(expr)) expr = expr.slice(0, expr.search(/;\s*$/))
}
list[i] = '\u0001' + (pcex.push(expr.replace(/[\r\n]+/g, ' ').trim()) - 1) + _bp[1]
for (var i = 1; i < list.length; i += 2) {
expr = list[i]
if (expr[0] === '^')
expr = expr.slice(1)
else if (jsfn) {
var israw = expr[0] === '='
expr = jsfn(israw ? expr.slice(1) : expr, opts).trim()
if (expr.slice(-1) === ';') expr = expr.slice(0, -1)
if (israw) expr = '=' + expr
}
html = list.join('')
list[i] = '\u0001' + (pcex.push(expr.replace(/[\r\n]+/g, ' ').trim()) - 1) + _bp[1]
}
return html
html = list.join('')
}
return html
}
// Restores expressions hidden by splitHtml and escape literal internal brackets
function restoreExpr(html, pcex) {
if (pcex.length) {
html = html
.replace(/\u0001(\d+)/g, function (_, d) {
return _bp[0] + pcex[d].replace(/"/g, '&quot;')
})
}
return html
// Restores expressions hidden by splitHtml and escape literal internal brackets
function restoreExpr(html, pcex) {
if (pcex.length) {
html = html
.replace(/\u0001(\d+)/g, function (_, d) {
var expr = pcex[d]
if (expr[0] === '=') {
expr = expr.replace(brackets.R_STRINGS, function (qs) {
return qs //.replace(/&/g, '&amp;') // I don't know if this make sense
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
})
}
return pcex._bp[0] + expr
})
}
return html
}
//## HTML Compilation
//-------------------
//## HTML Compilation
//-------------------
// `HTML_TAGS` matches only start and self-closing tags, not the content.
var
HTML_COMMENT = /<!--(?!>)[\S\s]*?-->/g,
HTML_TAGS = /<([-\w]+)\s*([^"'\/>]*(?:(?:"[^"]*"|'[^']*'|\/[^>])[^'"\/>]*)*)(\/?)>/g
// `HTML_TAGS` matches only start and self-closing tags, not the content.
var
HTML_COMMENT = /<!--(?!>)[\S\s]*?-->/g,
HTML_TAGS = /<([-\w]+)\s*([^"'\/>]*(?:(?:"[^"]*"|'[^']*'|\/[^>])[^'"\/>]*)*)(\/?)>/g,
PRE_TAG = _regEx(
/<pre(?:\s+[^'">]+(?:(?:@Q)[^'">]*)*|\s*)?>([\S\s]*?)<\/pre\s*>/
.source.replace('@Q', brackets.R_STRINGS.source), 'gi')
/**
* Parses and formats the HTML text.
*
* @param {string} html - Can contain embedded HTML comments and literal whitespace
* @param {Object} opts - Collected user options. Includes the brackets array in `_bp`
* @param {Array} [pcex] - Keeps precompiled expressions
* @param {number} [intc] - 1 if internal call
* @returns {string} The parsed HTML text
* @see http://www.w3.org/TR/html5/syntax.html
*/
function compileHTML(html, opts, pcex, intc) {
/**
* Parses and formats the HTML text.
*
* @param {string} html - Can contain embedded HTML comments and literal whitespace
* @param {Object} opts - Collected user options. Includes the brackets array in `_bp`
* @param {Array} [pcex] - Keeps precompiled expressions
* @param {number} [intc] - 1 if internal call
* @returns {string} The parsed HTML text
* @see http://www.w3.org/TR/html5/syntax.html
*/
function compileHTML(html, opts, pcex /*, url*/) {
// `_bp`is undefined when `compileHTML` is called from tests
if (!intc) {
_bp = brackets.array(opts.brackets)
html = html.replace(/\r\n?/g, '\n').replace(HTML_COMMENT, '').replace(TRIM_TRAIL, '')
}
if (!pcex) pcex = []
var intf = (pcex || (pcex = []))._intflag
if (!intf)
html = html.replace(/\r\n?/g, '\n').replace(HTML_COMMENT, '').replace(TRIM_TRAIL, '')
// separate the expressions, then parse the tags and their attributes
html = splitHtml(html, opts, pcex)
.replace(HTML_TAGS, function (_, name, attr, ends) {
// force all tag names to lowercase
name = name.toLowerCase()
// close self-closing tag, except if this is a html5 void tag
ends = ends && !VOID_TAGS.test(name) ? '></' + name : ''
// format the attributes
if (attr) name += ' ' + parseAttrs(attr)
// `_bp` is undefined when `compileHTML` is not called by compile
if (!pcex._bp) pcex._bp = brackets.array(opts.brackets)
return '<' + name + ends + '>'
})
// separate the expressions, then parse the tags and their attributes
html = splitHtml(html, opts, pcex)
.replace(HTML_TAGS, function (_, name, attr, ends) {
// force all tag names to lowercase
name = name.toLowerCase()
// close self-closing tag, except if this is a html5 void tag
ends = ends && !VOID_TAGS.test(name) ? '></' + name : ''
// format the attributes
if (attr) name += ' ' + parseAttrs(attr, pcex)
// tags parsed, now compact whitespace if `opts.whitespace` is not set
if (!opts.whitespace) {
var p = [],
pre = /<pre(?:\s+[^'">]+(?:(?:"[^"]*"|'[^']*')[^'">]*)*|\s*)>[\s\S]*<\/pre\s*>/gi
return '<' + name + ends + '>'
})
html = html.replace(pre, function (q) {
return '\u0002' + (p.push(q) - 1) + '~' }).trim().replace(/\s+/g, ' ')
// tags parsed, now compact whitespace if `opts.whitespace` is not set
if (!opts.whitespace) {
if (/<pre[\s>]/.test(html)) {
var p = []
html = html.replace(PRE_TAG, function (q)
{ return p.push(q) && '\u0002' }).trim().replace(/\s+/g, ' ')
// istanbul ignore else
if (p.length)
html = html.replace(/\u0002(\d+)~/g, function (q, n) { return p[n] })
html = html.replace(/\u0002/g, function (_) { return p.shift() })
}
else
html = html.trim().replace(/\s+/g, ' ')
}
// for `opts.compact`, remove whitespace between tags
if (opts.compact) html = html.replace(/> <([-\w\/])/g, '><$1')
// for `opts.compact`, remove whitespace between tags
if (opts.compact) html = html.replace(/> <([-\w\/])/g, '><$1')
return restoreExpr(html, pcex)
}
return restoreExpr(html, pcex)
}
// JavaScript Compilation
// ----------------------
// JavaScript Compilation
// ----------------------
// JS_RMCOMMS prepares regexp for remotion of multiline and single-line comments
// JS_ES6SIGN matches es6 methods across multiple lines up to their first curly brace
var
JS_RMCOMMS = _regEx('(' + brackets.S_QBLOCKS + ')|' + brackets.R_MLCOMMS.source + '|//[^\r\n]*', 'g'),
JS_ES6SIGN = /^([ \t]*)([$_A-Za-z][$\w]*)\s*(\([^()]*\)\s*{)/m
// Default parser for JavaScript code
function riotjs(js) {
var
// Prepare regexp for remotion of multiline and single-line comments
JS_RMCOMMS = _regEx(
'(' + brackets.S_QBLOCKS + ')|' + brackets.R_MLCOMMS.source + '|//[^\r\n]*',
'g'),
// Matches es6 methods across multiple lines up to their first curly brace
JS_ES6SIGN = /^([ \t]*)([$_A-Za-z][$\w]*)\s*(\([^()]*\)\s*{)/m
match,
toes5,
parts = [], // parsed code
pos
// Default parser for JavaScript code
function riotjs(js) {
var
match,
toes5,
parts = [], // parsed code
pos
// remove comments
js = js.replace(JS_RMCOMMS, function (m, q) { return q ? m : ' ' })
// remove comments
js = js.replace(JS_RMCOMMS, function (m, q) { return q ? m : ' ' })
// $1: indentation,
// $2: method name,
// $3: parameters
while (match = js.match(JS_ES6SIGN)) {
// $1: indentation,
// $2: method name,
// $3: parameters
while (match = js.match(JS_ES6SIGN)) {
// save remaining part now -- IE9 changes `rightContext` in `RegExp.test`
parts.push(RegExp.leftContext)
js = RegExp.rightContext
pos = skipBlock(js) // find the closing bracket
// save remaining part now -- IE9 changes `rightContext` in `RegExp.test`
parts.push(RegExp.leftContext)
js = RegExp.rightContext
pos = skipBlock(js) // find the closing bracket
// convert ES6 method signature to ES5 function
toes5 = !/^(?:if|while|for|switch|catch|function)$/.test(match[2])
if (toes5)
match[0] = match[1] + 'this.' + match[2] + ' = function' + match[3]
// convert ES6 method signature to ES5 function
toes5 = !/^(?:if|while|for|switch|catch|function)$/.test(match[2])
if (toes5)
match[0] = match[1] + 'this.' + match[2] + ' = function' + match[3]
parts.push(match[0], js.slice(0, pos))
js = js.slice(pos)
if (toes5 && !/^\s*.\s*bind\b/.test(js)) parts.push('.bind(this)')
}
parts.push(match[0], js.slice(0, pos))
js = js.slice(pos)
if (toes5 && !/^\s*.\s*bind\b/.test(js)) parts.push('.bind(this)')
}
return parts.length ? parts.join('') + js : js
return parts.length ? parts.join('') + js : js
// Inner helper - find the position following the closing bracket for the current block
function skipBlock(str) {
var
re = _regEx('([{}])|' + brackets.S_QBLOCKS, 'g'),
level = 1,
match
// Inner helper - find the position following the closing bracket for the current block
function skipBlock(str) {
var
re = _regEx('([{}])|' + brackets.S_QBLOCKS, 'g'),
level = 1,
match
while (level && (match = re.exec(str))) {
if (match[1])
match[1] === '{' ? ++level : --level
}
return level ? str.length : re.lastIndex
while (level && (match = re.exec(str))) {
if (match[1])
match[1] === '{' ? ++level : --level
}
return level ? str.length : re.lastIndex
}
}
/**
* Runs the parser for the JavaScript code, defaults to `riotjs`
*
* @param {string} js - Buffer with the javascript code
* @param {Object} opts - Options, can include a custom parser function
* @param {string} type - Optional type for parser selection
* @param {Object} parserOpts - Optional parser options
* @returns {string} The parsed JavaScript code
*/
function compileJS(js, opts, type, parserOpts) {
if (!js) return ''
if (!type) type = opts.type
/**
* Runs the parser for the JavaScript code, defaults to `riotjs`
*
* @param {string} js - Buffer with the javascript code
* @param {Object} opts - Options, can include a custom parser function
* @param {string} [type] - Optional type for parser selection
* @param {Object} [parserOpts] - User options for the parser
* @param {string} [url] - url of the file being compiled
* @returns {string} The parsed JavaScript code
*/
function compileJS(js, opts, type, parserOpts, url) {
if (!js) return ''
if (!type) type = opts.type
var parser = opts.parser || (type ? parsers.js[type] : riotjs)
if (!parser)
throw new Error('JS parser not found: "' + type + '"')
var parser = opts.parser || (type ? parsers.js[type] : riotjs)
if (!parser)
throw new Error('JS parser not found: "' + type + '"')
return parser(js, parserOpts).replace(TRIM_TRAIL, '')
}
return parser(js, parserOpts, url).replace(TRIM_TRAIL, '')
}
// CSS Compilation
// ----------------
// See http://www.w3.org/TR/CSS21/
// CSS Compilation
// ----------------
// See http://www.w3.org/TR/CSS21/
// Prepare regex to match CSS selectors, excluding those beginning with '@'.
var CSS_SELECTOR = _regEx('(}|{|^)[ ;]*([^@ ;{}][^{}]*)(?={)|' + brackets.R_STRINGS.source, 'g')
// Prepare regex to match CSS selectors, excluding those beginning with '@'.
var CSS_SELECTOR = _regEx('(}|{|^)[ ;]*([^@ ;{}][^{}]*)(?={)|' + brackets.R_STRINGS.source, 'g')
// Parses styles enclosed in a "scoped" tag (`scoped` is deprecated in HTML5).
// The "style" string is received without comments or surrounding spaces.
function scopedCSS(tag, style) {
var scope = ':scope'
// Parses styles enclosed in a "scoped" tag (`scoped` is deprecated in HTML5).
// The "style" string is received without comments or surrounding spaces.
function scopedCSS(tag, style) {
var scope = ':scope'
return style.replace(CSS_SELECTOR, function (m, p1, p2) {
// skip quoted strings
if (!p2) return m
return style.replace(CSS_SELECTOR, function (m, p1, p2) {
// skip quoted strings
if (!p2) return m
// we have a selector list, parse each individually
p2 = p2.replace(/[^,]+/g, function (sel) {
var s = sel.trim()
// skips the keywords and percents of css animations
if (s && s !== 'from' && s !== 'to' && s.slice(-1) !== '%') {
// replace the `:scope` pseudo-selector, where it is, with the root tag name;
// if `:scope` was not included, add the tag name as prefix, and mirror all
// to `[riot-tag]`
if (s.indexOf(scope) < 0) s = scope + ' ' + s
s = s.replace(scope, tag) + ',' +
s.replace(scope, '[riot-tag="' + tag + '"]')
}
return sel.slice(-1) === ' ' ? s + ' ' : s // respect (a little) the user style
})
// add the danling bracket char and return the processed selector list
return p1 ? p1 + ' ' + p2 : p2
// we have a selector list, parse each individually
p2 = p2.replace(/[^,]+/g, function (sel) {
var s = sel.trim()
// skips the keywords and percents of css animations
if (s && s !== 'from' && s !== 'to' && s.slice(-1) !== '%') {
// replace the `:scope` pseudo-selector, where it is, with the root tag name;
// if `:scope` was not included, add the tag name as prefix, and mirror all
// to `[riot-tag]`
if (s.indexOf(scope) < 0) s = scope + ' ' + s
s = s.replace(scope, tag) + ',' +
s.replace(scope, '[riot-tag="' + tag + '"]')
}
return sel.slice(-1) === ' ' ? s + ' ' : s // respect (a little) the user style
})
}
// add the danling bracket char and return the processed selector list
return p1 ? p1 + ' ' + p2 : p2
})
}
/**
* Runs the parser for style blocks.
* For scoped styles, the `scopedCSS` function is called after any external parser.
*
* @param {string} style - Raw style block
* @param {string} tag - The root tag name to which the style belongs
* @param {string} [type] - One of `parsers.css`: jade, stylus, etc.
* @param {boolean} [scoped] - `true` for scoped styles
* @param {object} opts - get the custom parser options
* @returns {string} The processed style block
*/
function compileCSS(style, tag, type, scoped, opts) {
/**
* Runs the parser for style blocks.
* For scoped styles, the `scopedCSS` function is called after any external parser.
*
* @param {string} style - Raw style block
* @param {string} tag - The root tag name to which the style belongs
* @param {string} [type] - One of `parsers.css`: jade, stylus, etc.
* @param {boolean} [scoped] - `true` for scoped styles
* @param {object} opts - get the custom parser options
* @returns {string} The processed style block
*/
function compileCSS(style, tag, type, scoped, opts) {
if (type) {
if (type === 'scoped-css') { // DEPRECATED
scoped = true
}
else if (parsers.css[type]) {
style = parsers.css[type](tag, style, opts)
}
else if (type !== 'css') {
throw new Error('CSS parser not found: "' + type + '"')
}
if (type) {
if (type === 'scoped-css') { // DEPRECATED
scoped = true
}
else if (parsers.css[type]) {
style = parsers.css[type](tag, style, opts)
}
else if (type !== 'css') {
throw new Error('CSS parser not found: "' + type + '"')
}
}
// remove comments, compact and trim whitespace
style = style.replace(brackets.R_MLCOMMS, '').replace(/\s+/g, ' ').trim()
// remove comments, compact and trim whitespace
style = style.replace(brackets.R_MLCOMMS, '').replace(/\s+/g, ' ').trim()
// translate scoped rules if nedded
return scoped ? scopedCSS(tag, style) : style
}
// translate scoped rules if nedded
return scoped ? scopedCSS(tag, style) : style
}
// The main compiler
// -----------------
// The main compiler
// -----------------
// Matches the 'type' attribute, for script and style tags
var
TYPE_ATTR = /\stype\s*=\s*(?:(['"])(.+?)\1|(\S+))/i, // don't handle escaped quotes :(
MISC_ATTR = /\s*=\s*("(?:\\[\S\s]|[^"\\]*)*"|'(?:\\[\S\s]|[^'\\]*)*'|\{[^}]+}|\S+)/.source
// TYPE_ATTR matches the 'type' attribute, for script and style tags
var
TYPE_ATTR = /\stype\s*=\s*(?:(['"])(.+?)\1|(\S+))/i, // don't handle escaped quotes :(
MISC_ATTR = /\s*=\s*("(?:\\[\S\s]|[^"\\]*)*"|'(?:\\[\S\s]|[^'\\]*)*'|\{[^}]+}|\S+)/.source
// Returns the value of the 'type' attribute, with the prefix "text/" removed.
function getType(str) {
// Returns the value of the 'type' attribute, with the prefix "text/" removed.
function getType(str) {
if (str) {
var match = str.match(TYPE_ATTR)
str = match && (match[2] || match[3])
}
return str ? str.replace('text/', '') : ''
if (str) {
var match = str.match(TYPE_ATTR)
str = match && (match[2] || match[3])
}
return str ? str.replace('text/', '') : ''
}
// Returns the value of any attribute, or the empty string for missing attribute.
function getAttr(str, name) {
// Returns the value of any attribute, or the empty string for missing attribute.
function getAttr(str, name) {
if (str) {
var
re = _regEx('\\s' + name + MISC_ATTR, 'i'),
match = str.match(re)
str = match && match[1]
if (str)
return /^['"]/.test(str) ? str.slice(1, -1) : str
}
return ''
if (str) {
var
re = _regEx('\\s' + name + MISC_ATTR, 'i'),
match = str.match(re)
str = match && match[1]
if (str)
return (/^['"]/).test(str) ? str.slice(1, -1) : str
}
return ''
}
// get the parser options from the options attribute
function getParserOptions(attrs) {
var opts = getAttr(attrs, 'options')
// convert the string into a valid js object
if (opts) opts = JSON.parse(opts)
return opts
}
// get the parser options from the options attribute
function getParserOptions(attrs) {
var opts = getAttr(attrs, 'options')
// convert the string into a valid js object
if (opts) opts = JSON.parse(opts)
return opts
}
// Runs the custom or default parser on the received JavaScript code.
// The CLI version can read code from the file system (experimental)
function getCode(code, opts, attrs, url) {
var type = getType(attrs),
parserOpts = getParserOptions(attrs)
// Runs the custom or default parser on the received JavaScript code.
// The CLI version can read code from the file system (experimental)
function getCode(code, opts, attrs, url) {
var type = getType(attrs),
parserOpts = getParserOptions(attrs)
//#if READ_JS_SRC
//#if NODE
if (url) {
var src = getAttr(attrs, 'src')
if (src && url) {
if (src) {
var
charset = getAttr(attrs, 'charset'),
file = path.resolve(path.dirname(url), src)
code = require('fs').readFileSync(file, {encoding: charset || 'utf8'})
code = require('fs').readFileSync(file, charset || 'utf8')
}
//#endif
return compileJS(code, opts, type, parserOpts)
}
//#endif
return compileJS(code, opts, type, parserOpts, url)
}
// Matches HTML tag ending a line. This regex still can be fooled by code as:
// ```js
// x <y && y >
// z
// ```
var END_TAGS = /\/>\n|^<(?:\/[\w\-]+\s*|[\w\-]+(?:\s+(?:[-\w:\xA0-\xFF][\S\s]*?)?)?)>\n/
// Matches HTML tag ending a line. This regex still can be fooled by code as:
// ```js
// x <y && y >
// z
// ```
var END_TAGS = /\/>\n|^<(?:\/[\w\-]+\s*|[\w\-]+(?:\s+(?:[-\w:\xA0-\xFF][\S\s]*?)?)?)>\n/
function splitBlocks(str) {
var k, m
function splitBlocks(str) {
var k, m
/* istanbul ignore next: this if() can't be true, but just in case... */
if (str[str.length - 1] === '>')
return [str, '']
/* istanbul ignore next: this if() can't be true, but just in case... */
if (str[str.length - 1] === '>') return [str, '']
k = str.lastIndexOf('<') // first probable open tag
while (~k) {
if (m = str.slice(k).match(END_TAGS)) {
k += m.index + m[0].length
return [str.slice(0, k), str.slice(k)]
}
k = str.lastIndexOf('<', k -1)
k = str.lastIndexOf('<') // first probable open tag
while (~k) {
if (m = str.slice(k).match(END_TAGS)) {
k += m.index + m[0].length
return [str.slice(0, k), str.slice(k)]
}
return ['', str]
k = str.lastIndexOf('<', k -1)
}
return ['', str]
}
// Runs the external HTML parser for the entire tag file
function compileTemplate(lang, html, opts) {
var parser = parsers.html[lang]
// Runs the external HTML parser for the entire tag file
function compileTemplate(html, url, lang, opts) {
var parser = parsers.html[lang]
if (!parser)
throw new Error('Template parser not found: "' + lang + '"')
if (!parser)
throw new Error('Template parser not found: "' + lang + '"')
return parser(html, opts)
}
return parser(html, opts, url)
}
/*
CUST_TAG regex don't allow unquoted expressions containing the `>` operator.
STYLE and SCRIPT disallows the operator `>` at all.
/*
CUST_TAG regex don't allow unquoted expressions containing the `>` operator.
STYLE and SCRIPT disallows the operator `>` at all.
The beta.4 CUST_TAG regex is fast, with RegexBuddy I get 76 steps and 14 backtracks on
the test/specs/fixtures/treeview.tag :) but fails with nested tags of the same name :(
With a greedy * operator, we have ~500 and 200bt, it is acceptable. So let's fix this.
*/
The beta.4 CUST_TAG regex is fast, with RegexBuddy I get 76 steps and 14 backtracks on
the test/specs/fixtures/treeview.tag :) but fails with nested tags of the same name :(
With a greedy * operator, we have ~500 and 200bt, it is acceptable. So let's fix this.
*/
// TODO: CUST_TAG fails with unescaped regex in the root attributes having '>' characters.
// We need brackets.split here?
var
CUST_TAG = _regEx(
/^([ \t]*)<([-\w]+)(?:\s+([^'"\/>]+(?:(?:@Q|\/[^>])[^'"\/>]*)*)|\s*)?(?:\/>|>[ \t]*\n?([\S\s]*)^\1<\/\2\s*>|>(.*)<\/\2\s*>)/
.source.replace('@Q', brackets.R_STRINGS.source), 'gim'),
STYLE = /<style(\s+[^>]*)?>\n?([^<]*(?:<(?!\/style\s*>)[^<]*)*)<\/style\s*>/gi,
SCRIPT = _regEx(STYLE.source.replace(/tyle/g, 'cript'), 'gi')
/**
* The main compiler processes all custom tags, one by one.
*
* In .tag files, a custom tag can span multiple lines, but there should be no other
* external element sharing the starting and ending lines of the tag (HTML comments
* inclusive) and should not be indented.
* Custom tags in HTML files don't have this restriction.
*
* @param {string} src - String with zero or more custom riot tags.
* @param {Object} [opts] - User options.
* @param {string} [url] - Filename of the riot tag, prepended to the generated code.
* @returns {string} JavaScript code to build the tag later, through riot.tag2 function.
*/
function compile(src, opts, url) {
var
CUST_TAG = /^([ \t]*)<([-\w]+)(?:\s+([^'"\/>]+(?:(?:"[^"]*"|'[^']*'|\/[^>])[^'"\/>]*)*)|\s*)?(?:\/>|>[ \t]*\n?([\s\S]*)^\1<\/\2\s*>|>(.*)<\/\2\s*>)/gim,
STYLE = /<style(\s+[^>]*)?>\n?([^<]*(?:<(?!\/style\s*>)[^<]*)*)<\/style\s*>/gi,
SCRIPT = _regEx(STYLE.source.replace(/tyle/g, 'cript'), 'gi')
parts = [],
exclude
/*
An alternate routine for finding custom tags, not depending in indentation.
Find openning tag, must be the first token in a line, after any whitespace.
if it is a self-close tag, i.e. ends with />. we are done.
Set counter to 1.
Find the next '<tag' or '</tag', if '<tag' found increment counter, else decrement.
When counter reach 0 we are done.
var re1 = _regEx('<' + tagName + '[\\s/>]', 'ig'), re2 = _regEx('</' + tagName + '[\\s>]', 'ig')
re1.lastIndex = TAG_START.lastIndex
while (match1 = re1.)
*/
if (!opts) opts = {}
/**
* The main compiler processes all custom tags, one by one.
*
* In .tag files, a custom tag can span multiple lines, but there should be no other
* external element sharing the starting and ending lines of the tag (HTML comments
* inclusive) and should not be indented.
* Custom tags in HTML files don't have this restriction.
*
* @param {string} src - String with zero or more custom riot tags.
* @param {Object} [opts] - User options.
* @param {string} [url] - Filename of the riot tag, prepended to the generated code.
* @returns {string} JavaScript code to build the tag later, through riot.tag2 function.
*/
function compile(src, opts, url) {
var label, parts = []
//#if NODE
// let's be sure that an url is always defined at least on node
// this will allow the babeljs users using the .babelrc file to configure
// their transpiler setup
url = url || process.cwd()
//#endif
if (!opts) opts = {}
exclude = opts.exclude || false
function included(s) { return !(exclude && ~exclude.indexOf(s)) }
// get a static brackets array for use on the entire source
_bp = brackets.array(opts.brackets)
// get a static brackets array for use on the entire source
var _bp = brackets.array(opts.brackets)
// run any custom html parser before the compilation
if (opts.template)
src = compileTemplate(opts.template, src, opts.templateOptions)
// run any custom html parser before the compilation
if (opts.template)
src = compileTemplate(src, url, opts.template, opts.templateOptions)
// riot #1070 -- isAbsolute does not exists in node 10.x
//#if NODE
/* istanbul ignore next */
label = !url ? '' : path.isAbsolute(url) ? path.relative('.', url) : url
if (label)
label = '//src: ' + label.replace(/\\/g, '/') + '\n'
//#else
label = url ? '//src: ' + url + '\n' : ''
//#endif
// normalize eols and start processing the tags
src = src
.replace(/\r\n?/g, '\n')
.replace(CUST_TAG, function (_, indent, tagName, attribs, body, body2) {
// normalize eols and start processing the tags
src = label + src
.replace(/\r\n?/g, '\n')
.replace(CUST_TAG, function (_, indent, tagName, attribs, body, body2) {
// content can have attributes first, then html markup with zero or more script or
// style tags of different types, and finish with an untagged block of javascript code.
var
jscode = '',
styles = '',
html = '',
pcex = []
// content can have attributes first, then html markup with zero or more script or
// style tags of different types, and finish with an untagged block of javascript code.
var
jscode = '',
styles = '',
html = '',
pcex = []
pcex._bp = _bp // local copy, in preparation for async compilation
pcex._intflag = 1
tagName = tagName.toLowerCase()
tagName = tagName.toLowerCase()
// process the attributes, including their expressions
attribs = !attribs ? '' : restoreExpr(parseAttrs(splitHtml(attribs, opts, pcex)), pcex)
// process the attributes, including their expressions
attribs = attribs && included('attribs') ?
restoreExpr(parseAttrs(splitHtml(attribs, opts, pcex), pcex), pcex) : ''
if (body2) body = body2
if (body2) body = body2
// remove comments and trim trailing whitespace
if (body && (body = body.replace(HTML_COMMENT, '')) && /\S/.test(body)) {
// remove comments and trim trailing whitespace
if (body && (body = body.replace(HTML_COMMENT, '')) && /\S/.test(body)) {
if (body2)
html = compileHTML(body2, opts, pcex, 1)
else {
body = body.replace(_regEx('^' + indent, 'gm'), '')
if (body2) {
/* istanbul ignore next */
html = included('html') ? compileHTML(body2, opts, pcex, 1, url) : ''
}
else {
body = body.replace(_regEx('^' + indent, 'gm'), '')
// get and process the style blocks
body = body.replace(STYLE, function (_, _attrs, _style) {
var scoped = _attrs && /\sscoped(\s|=|$)/i.test(_attrs),
csstype = getType(_attrs) || opts.style
styles += (styles ? ' ' : '') +
compileCSS(_style, tagName, csstype, scoped, getParserOptions(_attrs))
return ''
})
// get and process the style blocks
// now the script blocks
body = body.replace(SCRIPT, function (_, _attrs, _script) {
//#if READ_JS_SRC
jscode += (jscode ? '\n' : '') + getCode(_script, opts, _attrs, url)
//#else
jscode += (jscode ? '\n' : '') + getCode(_script, opts, _attrs)
//#endif
return ''
})
body = body.replace(STYLE, included('css') ? function (_, _attrs, _style) {
var scoped = _attrs && /\sscoped(\s|=|$)/i.test(_attrs),
csstype = getType(_attrs) || opts.style
styles += (styles ? ' ' : '') +
compileCSS(_style, tagName, csstype, scoped, getParserOptions(_attrs), url)
return ''
} : '')
// separate the untagged javascript block from the html markup
var blocks = splitBlocks(body.replace(TRIM_TRAIL, ''))
// now the script blocks
body = body.replace(SCRIPT, included('js') ? function (_, _attrs, _script) {
jscode += (jscode ? '\n' : '') + getCode(_script, opts, _attrs, url)
return ''
} : '')
// separate the untagged javascript block from the html markup
var blocks = splitBlocks(body.replace(TRIM_TRAIL, ''))
if (included('html')) {
body = blocks[0]
if (body)
html = compileHTML(body, opts, pcex, 1)
}
if (included('js')) {
body = blocks[1]
if (/\S/.test(body))
jscode += (jscode ? '\n' : '') + compileJS(body, opts)
jscode += (jscode ? '\n' : '') + compileJS(body, opts, null, null, url)
}
}
}
// give more consistency to the output
jscode = /\S/.test(jscode) ? jscode.replace(/\n{3,}/g, '\n\n') : ''
// give more consistency to the output
jscode = /\S/.test(jscode) ? jscode.replace(/\n{3,}/g, '\n\n') : ''
// replace the tag with a call to the riot.tag2 function and we are done
if (opts.entities) {
parts.push({
tagName: tagName,
html: html,
css: styles,
attribs: attribs,
js: jscode
})
return ''
}
// replace the tag with a call to the riot.tag2 function and we are done
return mktag(tagName, html, styles, attribs, jscode, pcex)
})
// replace the tag with a call to the riot.tag2 function and we are done
if (opts.entities) {
parts.push({
tagName: tagName,
html: html,
css: styles,
attribs: attribs,
js: jscode
})
return ''
}
return opts.entities ? parts : src
}
// replace the tag with a call to the riot.tag2 function and we are done
return mktag(tagName, html, styles, attribs, jscode, pcex)
})
//#if RIOT
return compile
if (opts.entities) return parts
})()
//#endif
//#if NODE
// Note: isAbsolute does not exists in node 10.x
if (url && opts.debug) {
/* istanbul ignore if */
if (path.isAbsolute(url)) url = path.relative('.', url)
src = '//src: ' + url.replace(/\\/g, '/') + '\n' + src
}
//#endif
return src
}

@@ -14,5 +14,6 @@ /**

*/
//#if NODE
function _try(name, req) { //eslint-disable-line complexity
var parser
//#if NODE
function fn(r) {

@@ -30,3 +31,3 @@ try {

case 'es6':
/* istanbul ignore next */
/* istanbul ignore next */
return fn('babel') || fn('babel-core') // versions 5.8x

@@ -46,3 +47,2 @@ case 'babel':

break
/* istanbul ignore next */
case 'scss':

@@ -56,6 +56,4 @@ case 'sass':

}
return fn(req)
}
parser = fn(req)
//#else
function _try(name, req) { //eslint-disable-line no-redeclare

@@ -73,6 +71,11 @@ switch (name) {

}
return _mods[name] = window[req]
}
parser = window[req]
if (!parser)
throw new Error(req + ' parser not found.')
//#endif
return parser
}
// Returns a parser instance, null if the parser is not found.

@@ -87,4 +90,8 @@ // Public through the parsers._get function.

var _html = {
jade: function (html, opts) {
return _req('jade').render(html, extend({pretty: true, doctype: 'html'}, opts))
jade: function (html, opts, url) {
return _req('jade').render(html, extend({
pretty: true,
filename: url,
doctype: 'html'
}, opts))
}

@@ -95,3 +102,3 @@ }

//#if NODE
sass: function(tag, css, opts) { // there's no standard sass for browsers
sass: function(tag, css, opts, url) { // there's no standard sass for browsers
var sass = _req('sass')

@@ -106,3 +113,3 @@

},
scss: function(tag, css, opts) { // there's no standard sass for browsers
scss: function(tag, css, opts, url) { // there's no standard sass for browsers
var sass = _req('sass')

@@ -117,3 +124,4 @@

},
less: function(tag, css, opts) {
//#endif
less: function(tag, css, opts, url) {
var less = _req('less'),

@@ -132,4 +140,3 @@ ret

},
//#endif
stylus: function (tag, css, opts) {
stylus: function (tag, css, opts, url) {
var

@@ -144,12 +151,12 @@ stylus = _req('stylus'), nib = _req('nib') // optional nib support

var _js = {
none: function (js, opts) {
none: function (js, opts, url) {
return js
},
livescript: function (js, opts) {
livescript: function (js, opts, url) {
return _req('livescript').compile(js, extend({bare: true, header: false}, opts))
},
typescript: function (js, opts) {
typescript: function (js, opts, url) {
return _req('typescript')(js, opts).replace(/\r\n?/g, '\n')
},
es6: function (js, opts) {
es6: function (js, opts, url) {
return _req('es6').transform(js, extend({

@@ -159,11 +166,10 @@ blacklist: ['useStrict', 'strict', 'react'], sourceMaps: false, comments: false

},
babel: function (js, opts) {
js = 'function __parser_babel_wrapper__(){' + js + '}'
babel: function (js, opts, url) {
return _req('babel').transform(js,
extend({
presets: ['es2015']
filename: url
}, opts)
).code.replace(/["']use strict["'];[\r\n]+/, '').slice(38, -2)
).code
},
coffee: function (js, opts) {
coffee: function (js, opts, url) {
return _req('coffee').compile(js, extend({bare: true}, opts))

@@ -179,5 +185,1 @@ }

})()
//#if RIOT
riot.parsers = parsers
//#endif

@@ -52,3 +52,6 @@

function compileTag(src, opts, url) {
globalEval(compile(src, opts, url))
var code = compile(src, opts)
// Implements #1070 by @beders, for .tag files.
if (url) code += '\n//# sourceURL=' + url + '.js'
globalEval(code)
if (!--scriptsAmount) done()

@@ -55,0 +58,0 @@ }

{
"name": "riot-compiler",
"version": "2.3.13",
"version": "2.3.16-beta",
"description": "Compiler for riot .tag files",
"main": "dist/compiler.js",
"jsnext:main": "dist/es6.compiler.js",
"directories": {

@@ -31,11 +32,11 @@ "lib": "lib",

"dependencies": {
"riot-tmpl": "^2.3.12"
"riot-tmpl": "^2.3.17"
},
"devDependencies": {
"coveralls": "^2.11.4",
"eslint": "^1.7.3",
"eslint": "^1.10.3",
"expect.js": "^0.3.1",
"istanbul": "^0.4.0",
"jspreproc": "^0.2.4",
"mocha": "^2.3.3"
"istanbul": "^0.4.1",
"jspreproc": "^0.2.5",
"mocha": "^2.3.4"
},

@@ -42,0 +43,0 @@ "author": "Muut, Inc. and other contributors",

@@ -32,15 +32,15 @@ [![Build Status][travis-image]][travis-url]

* es6 - For `babel` and `babel-core` v5.8.x and below
* babel - For `babel-core` v6.x - You must `npm install babel-preset-es2015` too, for this works.
* babel - For `babel-core` v6.x - You must `npm install babel-preset-es2015` too, for this to work.
[travis-image]:https://img.shields.io/travis/riot/compiler.svg?style=flat-square
[travis-url]:https://travis-ci.org/riot/compiler
[license-image]:http://img.shields.io/badge/license-MIT-000000.svg?style=flat-square
[license-url]:LICENSE.txt
[npm-version-image]:http://img.shields.io/npm/v/riot-compiler.svg?style=flat-square
[npm-downloads-image]:http://img.shields.io/npm/dm/riot-compiler.svg?style=flat-square
[npm-url]:https://npmjs.org/package/riot-compiler
[coverage-image]:https://img.shields.io/coveralls/riot/compiler/master.svg?style=flat-square
[coverage-url]:https://coveralls.io/r/riot/compiler/?branch=master
[codeclimate-image]:https://img.shields.io/codeclimate/github/riot/compiler.svg?style=flat-square
[codeclimate-url]:https://codeclimate.com/github/riot/compiler
[travis-image]: https://img.shields.io/travis/riot/compiler.svg?style=flat-square
[travis-url]: https://travis-ci.org/riot/compiler
[license-image]: http://img.shields.io/badge/license-MIT-000000.svg?style=flat-square
[license-url]: LICENSE.txt
[npm-version-image]: http://img.shields.io/npm/v/riot-compiler.svg?style=flat-square
[npm-downloads-image]: http://img.shields.io/npm/dm/riot-compiler.svg?style=flat-square
[npm-url]: https://npmjs.org/package/riot-compiler
[coverage-image]: https://img.shields.io/coveralls/riot/compiler/master.svg?style=flat-square
[coverage-url]: https://coveralls.io/r/riot/compiler/?branch=master
[codeclimate-image]: https://codeclimate.com/github/riot/compiler/badges/gpa.svg
[codeclimate-url]: https://codeclimate.com/github/riot/compiler
//src: test/specs/fixtures/oneline.tag
riot.tag2('oneline', '<p>one line</p>', '', '', function(opts) {
riot.tag2('oneline1', '<p>one line</p>', '', 'id="1"', function(opts) {
});
riot.tag2('oneline2', '', '', 'id="2"', function(opts) {
});
//src: test/specs/fixtures/pre.tag
riot.tag2('pre', '<pre>xyz\n cc\n ss</pre>', '', '', function(opts) {
});
riot.tag2('pre-tag', '<pre>xyz\n cc\n ss</pre> <pre-fake>xyz cc ss</pre-fake> <pre x="{1>2}">xyz\n cc\n ss</pre>', '', '', function(opts) {
}, '{ }');

@@ -1,2 +0,2 @@

riot.tag2('root-attribs', '', '', 'disabled="disabled" style="display:none" id="{_id}"', function(opts) {
riot.tag2('root-attribs', '', '', '__disabled="{x<y}" style="display:none" data-x="{x>0 ? 1 : 0}" data-y="{y<0 ? -1 : 0}" data-s="{\'John\\\'s\'}" id="{_id}"', function(opts) {
}, '{ }');

@@ -71,7 +71,7 @@

it('double quotes in expressions are converted to `&quot;`', function () {
testStr('<p x={ "a" } y="{2}">', '<p x="{&quot;a&quot;}" y="{2}">')
testStr('<p x="{"a"}" y="{2}">', '<p x="{&quot;a&quot;}" y="{2}">')
testStr('<p x=\'{"a"}\' y="{2}">', '<p x="{&quot;a&quot;}" y="{2}">')
testStr('<p x="{""}">', '<p x="{&quot;&quot;}">')
it('nested double quotes are supported in expressions', function () {
testStr('<p x={ "a" } y="{2}">', '<p x="{"a"}" y="{2}">')
testStr('<p x="{"a"}" y="{2}">', '<p x="{"a"}" y="{2}">')
testStr('<p x=\'{"a"}\' y="{2}">', '<p x="{"a"}" y="{2}">')
testStr('<p x="{""}">', '<p x="{""}">')
})

@@ -120,4 +120,13 @@

it('raw html detection through the `=` flag', function () {
testStr(
'<p>{= \'<\' + myElem + \' style="color: \' + myColor + \';">\\n Click me</\' + myElem + \'>\'}</p>',
'<p>{= \'&lt;\' + myElem + \' style="color: \' + myColor + \';"&gt;\\n Click me&lt;/\' + myElem + \'&gt;\'}</p>')
testStr(
'<ul><li>{= ["foo", "bar"].join(\'<br/>\') }</li></ul>',
'<ul><li>{= ["foo", "bar"].join(\'&lt;br/&gt;\')}</li></ul>')
})
})
})
riot.tag2('babel', '<h3>{test}</h3>', '', '', function(opts) {
var type = 'JavaScript';
this.test = 'This is ' + type;
'use strict';
var _foo = require('foo');
var type = 'JavaScript';
this.test = 'This is ' + type;
}, '{ }');

@@ -9,4 +9,4 @@ //

var
basedir = __dirname,
jsdir = path.join(basedir, 'js')
fixtures = __dirname,
expected = path.join(fixtures, 'js')

@@ -33,9 +33,14 @@ function have(mod, req) {

function testParser(name, opts) {
function testParser(name, opts, save) {
var
file = name + (opts.type ? '.' + opts.type : ''),
str1 = cat(basedir, file + '.tag'),
str2 = cat(jsdir, file + '.js')
str1 = cat(fixtures, file + '.tag'),
str2 = cat(expected, file + '.js')
js = compiler.compile(str1, opts || {}, path.join(fixtures, file + '.tag'))
expect(normalize(compiler.compile(str1, opts || {}))).to.be(normalize(str2))
if (save)
fs.writeFile(path.join(expected, file + '_out.js'), js, function (err) {
if (err) throw err
})
expect(normalize(js)).to.be(normalize(str2))
}

@@ -76,4 +81,4 @@

it('plays with quoted values', function () {
testStr('<a href={ "a" }>', '<a href="{@ &quot;a&quot;}">', opts)
testStr('<a>{"b"}</a>', '<a>{@&quot;b&quot;}</a>', opts)
testStr('<a href={ "a" }>', '<a href="{@ "a"}">', opts)
testStr('<a>{"b"}</a>', '<a>{@"b"}</a>', opts)
})

@@ -102,3 +107,3 @@

this.timeout(25000) // first call to babel-core is slooooow!
this.timeout(30000) // first call to babel-core is slooooow!

@@ -244,7 +249,7 @@ // complex.tag

it('Mixing CSS blocks with different type', function () {
it('mixing CSS blocks with different type', function () {
testParser('mixed-css', {})
})
it('The style option for setting the CSS parser (v2.3.13)', function () {
it('the style option for setting the CSS parser (v2.3.13)', function () {
var

@@ -269,5 +274,5 @@ source = [

it('Unknown HTML template parser throws an error', function () {
it('unknown HTML template parser throws an error', function () {
var
str1 = cat(basedir, 'test.tag')
str1 = cat(fixtures, 'test.tag')

@@ -277,5 +282,5 @@ expect(compiler.compile).withArgs(str1, {template: 'unknown'}).to.throwError()

it('Unknown JS & CSS parsers throws an error', function () {
it('unknown JS & CSS parsers throws an error', function () {
var
str1 = cat(basedir, 'test.tag'),
str1 = cat(fixtures, 'test.tag'),
str2 = [

@@ -297,2 +302,10 @@ '<error>',

it('emiting raw html through the `=` flag, with parser', function () {
// custom parser
compiler.parsers.js.rawhtml = function(js) {
return js.replace(/"/g, '&quot;').replace(/'/g, '"')
}
testParser('raw', { type: 'rawhtml', expr: true })
})
})

@@ -22,3 +22,3 @@ var fs = require('fs'),

function render(str, name) {
return compiler.compile(str, {}, path.join(fixtures, name))
return compiler.compile(str, { debug: true }, path.join(fixtures, name))
}

@@ -30,6 +30,10 @@

function testFile(name) {
function testFile(name, save) {
var src = cat(fixtures, name + '.tag'),
js = render(src, name + '.tag')
if (save)
fs.writeFile(path.join(expected, name + '_out.js'), js, function (err) {
if (err) throw err
})
expect(js).to.equal(cat(expected, name + '.js'))

@@ -120,2 +124,6 @@ }

it('Whitespace is compacted in other parts', function() {
testFile('whitespace')
})
it('Empty tag', function () {

@@ -138,4 +146,4 @@ testFile('empty')

it('the `entities` option give access to the compiled parts', function () {
var parts = compiler.compile(cat(fixtures, 'treeview.tag'), {entities: true})
it('The `entities` option give access to the compiled parts', function () {
var parts = compiler.compile(cat(fixtures, 'treeview.tag'), {entities: true}),
resarr = [

@@ -165,2 +173,54 @@ [ 'treeview',

it('The `exclude` option to ignore parts of the tag', function () {
var parts = compiler.compile(cat(fixtures, 'treeview.tag'), {
entities: true,
exclude: ['html', 'js']
}),
dummyTag = [
'<my-tag>',
'<p>{ hi }</p>',
'this.hi = "hi"',
'<style scoped>',
' :scope { color: red; }',
'</style>',
'</my-tag>'
].join('\n')
expect(compiler.compile(dummyTag, {
exclude: ['html']
})).to.be("riot.tag2('my-tag', '', 'my-tag,[riot-tag=\"my-tag\"] { color: red; }', '', function(opts) {\nthis.hi = \"hi\"\n\n});")
expect(compiler.compile(dummyTag, {
exclude: ['html', 'js']
})).to.be("riot.tag2('my-tag', '', 'my-tag,[riot-tag=\"my-tag\"] { color: red; }', '', function(opts) {\n});")
expect(compiler.compile(dummyTag, {
exclude: ['css']
})).to.be("riot.tag2('my-tag', '<p>{hi}</p>', '', '', function(opts) {\nthis.hi = \"hi\"\n\n}, '{ }');")
expect(parts[0].html + parts[1].html).to.be('')
expect(parts[0].js + parts[1].js).to.be('')
parts = compiler.compile(cat(fixtures, 'scoped.tag'), {
entities: true,
exclude: ['css']
})
expect(parts[0].html).to.not.match(/style/)
expect(parts[0].css).to.be('')
parts = compiler.compile(cat(fixtures, 'root-attribs.tag'), {
entities: true,
exclude: ['attribs']
})
expect(parts[0].attribs).to.be('')
})
it('Output an expression without evaluation by escaping the opening brace', function () {
testFile('print-brackets')
})
it('Escaping raw html in expressions through the `=` flag', function () {
testFile('raw-html')
})
})

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc