riot-compiler
Advanced tools
Comparing version 3.0.0-alpha.1 to 3.0.0-alpha.2
# Compiler Changes | ||
### v2.5.5 | ||
- Fix to erroneous version number in the package.json, v2.5.4 was released before. | ||
- Removed unuseful files from the npm package. | ||
- Updated credits in package.json | ||
- Updated devDependencies, skip ESLint in CI test for node v0.12 | ||
- BuGless-hack for [riot#1966](https://github.com/riot/riot/issues/1966) - You can use `<-/>` to signal the end of the html if your html is ending with an expression. | ||
### v2.5.4 | ||
- Fix #68 : SASS inside Pug template gives Invalid CSS. | ||
- Added parser for [bublé](https://buble.surge.sh) as `buble` in the browser. Option `modules` is `false` in all versions. | ||
- Added parser for [bublé](https://buble.surge.sh) as `buble`. | ||
- Added support for es6 `import` statements. Thanks to @kuashe! - Related to [riot#1715](https://github.com/riot/riot/issues/1715), [riot#1784](https://github.com/riot/riot/issues/1784), and [riot#1864](https://github.com/riot/riot/issues/1864). | ||
### v2.5.3 | ||
- Fix #73 : resolveModuleSource must be a function - Option removed from the default Babel options. | ||
- Updated node.js to 4.4 in the Travis environment. | ||
- Downgraded ESLint to 2.x for using with node v0.12.x | ||
### v2.5.2 | ||
- Fix #72: `undefined` is not a function when evaluating `parsers._req`. | ||
- Updated node versions for travis, including v5.x | ||
### v2.4.1 | ||
- Add the `pug` parser (it will replace completely `jade` in the next major release) | ||
- Add the possibility to pass custom parsers options directly via the `compiler.compile` method through the `parserOptions: {js: {}, template: {}, style: {}}` key [more info](https://github.com/riot/compiler/issues/64) | ||
- Fix un-escape parser options in html [more info](https://github.com/riot/compiler/issues/63) | ||
### v2.3.23 | ||
@@ -4,0 +32,0 @@ - The parsers are moved to its own directory in the node version. The load is on first use. |
/** | ||
* Compiler for riot custom tags | ||
* @version v3.0.0-alpha.1 | ||
* @version v3.0.0-alpha.2 | ||
*/ | ||
@@ -8,13 +8,41 @@ | ||
// istanbul ignore next | ||
function safeRegex (re) { | ||
var src = re.source | ||
var opt = re.global ? 'g' : '' | ||
if (re.ignoreCase) opt += 'i' | ||
if (re.multiline) opt += 'm' | ||
for (var i = 1; i < arguments.length; i++) { | ||
src = src.replace('@', '\\' + arguments[i]) | ||
} | ||
return new RegExp(src, opt) | ||
} | ||
/** | ||
* @module parsers | ||
*/ | ||
var parsers = (function () { | ||
var parsers = (function (win) { | ||
var _p = {} | ||
function _r (name) { | ||
var parser = win[name] | ||
if (parser) return parser | ||
throw new Error('Parser "' + name + '" not loaded.') | ||
} | ||
function _req (name) { | ||
var parser = window[name] | ||
var parts = name.split('.') | ||
if (parts.length !== 2) throw new Error('Bad format for parsers._req') | ||
var parser = _p[parts[0]][parts[1]] | ||
if (parser) return parser | ||
throw new Error(name + ' parser not found.') | ||
throw new Error('Parser "' + name + '" not found.') | ||
} | ||
@@ -34,65 +62,81 @@ | ||
var _p = { | ||
html: { | ||
jade: function (html, opts, url) { | ||
opts = extend({ | ||
pretty: true, | ||
filename: url, | ||
doctype: 'html' | ||
}, opts) | ||
return _req('jade').render(html, opts) | ||
} | ||
}, | ||
function renderPug (compilerName, html, opts, url) { | ||
opts = extend({ | ||
pretty: true, | ||
filename: url, | ||
doctype: 'html' | ||
}, opts) | ||
return _r(compilerName).render(html, opts) | ||
} | ||
css: { | ||
less: function (tag, css, opts, url) { | ||
var ret | ||
opts = extend({ | ||
sync: true, | ||
syncImport: true, | ||
filename: url | ||
}, opts) | ||
_req('less').render(css, opts, function (err, result) { | ||
// istanbul ignore next | ||
if (err) throw err | ||
ret = result.css | ||
}) | ||
return ret | ||
} | ||
_p.html = { | ||
jade: function (html, opts, url) { | ||
/* eslint-disable */ | ||
console.log('DEPRECATION WARNING: jade was renamed "pug" - The jade parser will be removed in riot@3.0.0!') | ||
/* eslint-enable */ | ||
return renderPug('jade', html, opts, url) | ||
}, | ||
pug: function (html, opts, url) { | ||
return renderPug('pug', html, opts, url) | ||
} | ||
} | ||
_p.css = { | ||
less: function (tag, css, opts, url) { | ||
var ret | ||
js: { | ||
es6: function (js, opts) { | ||
opts = extend({ | ||
blacklist: ['useStrict', 'strict', 'react'], | ||
sourceMaps: false, | ||
comments: false | ||
}, opts) | ||
return _req('babel').transform(js, opts).code | ||
}, | ||
babel: function (js, opts, url) { | ||
return _req('babel').transform(js, extend({ filename: url }, opts)).code | ||
}, | ||
coffee: function (js, opts) { | ||
return _req('CoffeeScript').compile(js, extend({ bare: true }, opts)) | ||
}, | ||
livescript: function (js, opts) { | ||
return _req('livescript').compile(js, extend({ bare: true, header: false }, opts)) | ||
}, | ||
typescript: function (js, opts) { | ||
return _req('typescript')(js, opts) | ||
}, | ||
none: function (js) { | ||
return js | ||
} | ||
opts = extend({ | ||
sync: true, | ||
syncImport: true, | ||
filename: url | ||
}, opts) | ||
_r('less').render(css, opts, function (err, result) { | ||
// istanbul ignore next | ||
if (err) throw err | ||
ret = result.css | ||
}) | ||
return ret | ||
} | ||
} | ||
_p.js = { | ||
es6: function (js, opts) { | ||
opts = extend({ | ||
blacklist: ['useStrict', 'strict', 'react'], | ||
sourceMaps: false, | ||
comments: false | ||
}, opts) | ||
return _r('babel').transform(js, opts).code | ||
}, | ||
babel: function (js, opts, url) { | ||
return _r('babel').transform(js, extend({ filename: url }, opts)).code | ||
}, | ||
buble: function (js, opts, url) { | ||
opts = extend({ | ||
source: url, | ||
modules: false | ||
}, opts) | ||
return _r('buble').transform(js, opts).code | ||
}, | ||
coffee: function (js, opts) { | ||
return _r('CoffeeScript').compile(js, extend({ bare: true }, opts)) | ||
}, | ||
livescript: function (js, opts) { | ||
return _r('livescript').compile(js, extend({ bare: true, header: false }, opts)) | ||
}, | ||
typescript: function (js, opts) { | ||
return _r('typescript')(js, opts) | ||
}, | ||
none: function (js) { | ||
return js | ||
} | ||
} | ||
_p.js.javascript = _p.js.none | ||
_p.js.coffeescript = _p.js.coffee | ||
_p._req = _req | ||
_p.utils = { | ||
extend: extend | ||
} | ||
return _p | ||
})() | ||
})(window || global) | ||
@@ -103,2 +147,5 @@ /** | ||
var extend = parsers.utils.extend | ||
/* eslint-enable */ | ||
var S_LINESTR = /"[^"\n\\]*(?:\\[\S\s][^"\n\\]*)*"|'[^'\n\\]*(?:\\[\S\s][^'\n\\]*)*'/.source | ||
@@ -124,7 +171,9 @@ | ||
var IMPORT_STATEMENT = /^\s*import(?:\s*[*{]|\s+[$_a-zA-Z'"]).*\n?/gm | ||
var TRIM_TRAIL = /[ \t]+$/gm | ||
var | ||
RE_HASEXPR = /\x01#\d/, | ||
RE_REPEXPR = /\x01#(\d+)/g, | ||
RE_HASEXPR = safeRegex(/@#\d/, 'x01'), | ||
RE_REPEXPR = safeRegex(/@#(\d+)/g, 'x01'), | ||
CH_IDEXPR = '\x01#', | ||
@@ -232,3 +281,18 @@ CH_DQCODE = '\u2057', | ||
function compileImports (js) { | ||
var imp = [] | ||
var imports = '' | ||
while (imp = IMPORT_STATEMENT.exec(js)) { | ||
imports += imp[0].trim() + '\n' | ||
} | ||
return imports | ||
} | ||
function rmImports (js) { | ||
var jsCode = js.replace(IMPORT_STATEMENT, '') | ||
return jsCode | ||
} | ||
function _compileHTML (html, opts, pcex) { | ||
if (!/\S/.test(html)) return '' | ||
@@ -343,7 +407,4 @@ html = splitHtml(html, opts, pcex) | ||
var parser = opts.parser || (type ? parsers.js[type] : riotjs) | ||
var parser = opts.parser || type && parsers._req('js.' + type, true) || riotjs | ||
if (!parser) { | ||
throw new Error('JS parser not found: "' + type + '"') | ||
} | ||
return parser(js, parserOpts, url).replace(/\r\n?/g, '\n').replace(TRIM_TRAIL, '') | ||
@@ -404,6 +465,6 @@ } | ||
scoped = true | ||
} else if (parsers.css[type]) { | ||
css = parsers.css[type](tag, css, opts.parserOpts || {}, opts.url) | ||
} else if (type !== 'css') { | ||
throw new Error('CSS parser not found: "' + type + '"') | ||
var parser = parsers._req('css.' + type, true) | ||
css = parser(tag, css, opts.parserOpts || {}, opts.url) | ||
} | ||
@@ -444,3 +505,3 @@ } | ||
function mktag (name, html, css, attr, js, opts) { | ||
function mktag (name, html, css, attr, js, imports, opts) { | ||
var | ||
@@ -452,3 +513,3 @@ c = opts.debug ? ',\n ' : ', ', | ||
return 'riot.tag2(\'' + name + SQ + | ||
return imports + 'riot.tag2(\'' + name + SQ + | ||
c + _q(html, 1) + | ||
@@ -470,3 +531,5 @@ c + _q(css) + | ||
k += m.index + m[0].length | ||
return [str.slice(0, k), str.slice(k)] | ||
m = str.slice(0, k) | ||
if (m.slice(-5) === '<-/>\n') m = m.slice(0, -5) | ||
return [m, str.slice(k)] | ||
} | ||
@@ -506,7 +569,7 @@ n = k | ||
return str | ||
.replace('&', /&/g) | ||
.replace('<', /</g) | ||
.replace('>', />/g) | ||
.replace('"', /"/g) | ||
.replace(''', /'/g) | ||
.replace(/&/g, '&') | ||
.replace(/</g, '<') | ||
.replace(/>/g, '>') | ||
.replace(/"/g, '"') | ||
.replace(/'/g, '\'') | ||
} | ||
@@ -523,14 +586,24 @@ | ||
type = getType(attribs), | ||
src = getAttrib(attribs, 'src') | ||
src = getAttrib(attribs, 'src'), | ||
jsParserOptions = extend({}, opts.parserOptions.js) | ||
if (src) return false | ||
return _compileJS(code, opts, type, getParserOptions(attribs), base) | ||
return _compileJS( | ||
code, | ||
opts, | ||
type, | ||
extend(jsParserOptions, getParserOptions(attribs)), | ||
base | ||
) | ||
} | ||
function cssCode (code, opts, attribs, url, tag) { | ||
var extraOpts = { | ||
parserOpts: getParserOptions(attribs), | ||
scoped: attribs && /\sscoped(\s|=|$)/i.test(attribs), | ||
url: url | ||
} | ||
var | ||
parserStyleOptions = extend({}, opts.parserOptions.style), | ||
extraOpts = { | ||
parserOpts: extend(parserStyleOptions, getParserOptions(attribs)), | ||
scoped: attribs && /\sscoped(\s|=|$)/i.test(attribs), | ||
url: url | ||
} | ||
@@ -541,7 +614,4 @@ return _compileCSS(code, tag, getType(attribs) || opts.style, extraOpts) | ||
function compileTemplate (html, url, lang, opts) { | ||
var parser = parsers.html[lang] | ||
if (!parser) { | ||
throw new Error('Template parser not found: "' + lang + '"') | ||
} | ||
var parser = parsers._req('html.' + lang, true) | ||
return parser(html, opts, url) | ||
@@ -562,6 +632,14 @@ } | ||
parts = [], | ||
included | ||
included, | ||
defaultParserptions = { | ||
template: {}, | ||
js: {}, | ||
style: {} | ||
} | ||
if (!opts) opts = {} | ||
opts.parserOptions = extend(defaultParserptions, opts.parserOptions || {}) | ||
included = opts.exclude | ||
@@ -575,3 +653,3 @@ ? function (s) { return opts.exclude.indexOf(s) < 0 } : function () { return 1 } | ||
if (opts.template) { | ||
src = compileTemplate(src, url, opts.template, opts.templateOptions) | ||
src = compileTemplate(src, url, opts.template, opts.parserOptions.template) | ||
} | ||
@@ -585,2 +663,3 @@ | ||
html = '', | ||
imports = '', | ||
pcex = [] | ||
@@ -632,3 +711,9 @@ | ||
body = _compileJS(blocks[1], opts, null, null, url) | ||
imports = compileImports(jscode) | ||
jscode = rmImports(jscode) | ||
if (body) jscode += (jscode ? '\n' : '') + body | ||
jscode = jscode.replace(IMPORT_STATEMENT, function (s) { | ||
imports += s.trim() + '\n' | ||
return '' | ||
}) | ||
} | ||
@@ -646,3 +731,4 @@ } | ||
attribs: attribs, | ||
js: jscode | ||
js: jscode, | ||
imports: imports | ||
}) | ||
@@ -652,3 +738,3 @@ return '' | ||
return mktag(tagName, html, styles, attribs, jscode, opts) | ||
return mktag(tagName, html, styles, attribs, jscode, imports, opts) | ||
}) | ||
@@ -661,3 +747,3 @@ | ||
var version = 'v3.0.0-alpha.1' | ||
var version = 'v3.0.0-alpha.2' | ||
@@ -664,0 +750,0 @@ export default { |
// istanbul ignore next | ||
function safeRegex (re) { | ||
var src = re.source | ||
var opt = re.global ? 'g' : '' | ||
if (re.ignoreCase) opt += 'i' | ||
if (re.multiline) opt += 'm' | ||
for (var i = 1; i < arguments.length; i++) { | ||
src = src.replace('@', '\\' + arguments[i]) | ||
} | ||
return new RegExp(src, opt) | ||
} | ||
/** | ||
* @module parsers | ||
*/ | ||
var parsers = (function () { | ||
var parsers = (function (win) { | ||
var _p = {} | ||
function _r (name) { | ||
var parser = win[name] | ||
if (parser) return parser | ||
throw new Error('Parser "' + name + '" not loaded.') | ||
} | ||
function _req (name) { | ||
var parser = window[name] | ||
var parts = name.split('.') | ||
if (parts.length !== 2) throw new Error('Bad format for parsers._req') | ||
var parser = _p[parts[0]][parts[1]] | ||
if (parser) return parser | ||
throw new Error(name + ' parser not found.') | ||
throw new Error('Parser "' + name + '" not found.') | ||
} | ||
@@ -27,65 +55,81 @@ | ||
var _p = { | ||
html: { | ||
jade: function (html, opts, url) { | ||
opts = extend({ | ||
pretty: true, | ||
filename: url, | ||
doctype: 'html' | ||
}, opts) | ||
return _req('jade').render(html, opts) | ||
} | ||
}, | ||
function renderPug (compilerName, html, opts, url) { | ||
opts = extend({ | ||
pretty: true, | ||
filename: url, | ||
doctype: 'html' | ||
}, opts) | ||
return _r(compilerName).render(html, opts) | ||
} | ||
css: { | ||
less: function (tag, css, opts, url) { | ||
var ret | ||
opts = extend({ | ||
sync: true, | ||
syncImport: true, | ||
filename: url | ||
}, opts) | ||
_req('less').render(css, opts, function (err, result) { | ||
// istanbul ignore next | ||
if (err) throw err | ||
ret = result.css | ||
}) | ||
return ret | ||
} | ||
_p.html = { | ||
jade: function (html, opts, url) { | ||
/* eslint-disable */ | ||
console.log('DEPRECATION WARNING: jade was renamed "pug" - The jade parser will be removed in riot@3.0.0!') | ||
/* eslint-enable */ | ||
return renderPug('jade', html, opts, url) | ||
}, | ||
pug: function (html, opts, url) { | ||
return renderPug('pug', html, opts, url) | ||
} | ||
} | ||
_p.css = { | ||
less: function (tag, css, opts, url) { | ||
var ret | ||
js: { | ||
es6: function (js, opts) { | ||
opts = extend({ | ||
blacklist: ['useStrict', 'strict', 'react'], | ||
sourceMaps: false, | ||
comments: false | ||
}, opts) | ||
return _req('babel').transform(js, opts).code | ||
}, | ||
babel: function (js, opts, url) { | ||
return _req('babel').transform(js, extend({ filename: url }, opts)).code | ||
}, | ||
coffee: function (js, opts) { | ||
return _req('CoffeeScript').compile(js, extend({ bare: true }, opts)) | ||
}, | ||
livescript: function (js, opts) { | ||
return _req('livescript').compile(js, extend({ bare: true, header: false }, opts)) | ||
}, | ||
typescript: function (js, opts) { | ||
return _req('typescript')(js, opts) | ||
}, | ||
none: function (js) { | ||
return js | ||
} | ||
opts = extend({ | ||
sync: true, | ||
syncImport: true, | ||
filename: url | ||
}, opts) | ||
_r('less').render(css, opts, function (err, result) { | ||
// istanbul ignore next | ||
if (err) throw err | ||
ret = result.css | ||
}) | ||
return ret | ||
} | ||
} | ||
_p.js = { | ||
es6: function (js, opts) { | ||
opts = extend({ | ||
blacklist: ['useStrict', 'strict', 'react'], | ||
sourceMaps: false, | ||
comments: false | ||
}, opts) | ||
return _r('babel').transform(js, opts).code | ||
}, | ||
babel: function (js, opts, url) { | ||
return _r('babel').transform(js, extend({ filename: url }, opts)).code | ||
}, | ||
buble: function (js, opts, url) { | ||
opts = extend({ | ||
source: url, | ||
modules: false | ||
}, opts) | ||
return _r('buble').transform(js, opts).code | ||
}, | ||
coffee: function (js, opts) { | ||
return _r('CoffeeScript').compile(js, extend({ bare: true }, opts)) | ||
}, | ||
livescript: function (js, opts) { | ||
return _r('livescript').compile(js, extend({ bare: true, header: false }, opts)) | ||
}, | ||
typescript: function (js, opts) { | ||
return _r('typescript')(js, opts) | ||
}, | ||
none: function (js) { | ||
return js | ||
} | ||
} | ||
_p.js.javascript = _p.js.none | ||
_p.js.coffeescript = _p.js.coffee | ||
_p._req = _req | ||
_p.utils = { | ||
extend: extend | ||
} | ||
return _p | ||
})() | ||
})(window || global) | ||
@@ -96,6 +140,9 @@ riot.parsers = parsers | ||
* Compiler for riot custom tags | ||
* @version v3.0.0-alpha.1 | ||
* @version v3.0.0-alpha.2 | ||
*/ | ||
var compile = (function () { | ||
var extend = parsers.utils.extend | ||
/* eslint-enable */ | ||
var S_LINESTR = /"[^"\n\\]*(?:\\[\S\s][^"\n\\]*)*"|'[^'\n\\]*(?:\\[\S\s][^'\n\\]*)*'/.source | ||
@@ -121,7 +168,9 @@ | ||
var IMPORT_STATEMENT = /^\s*import(?:\s*[*{]|\s+[$_a-zA-Z'"]).*\n?/gm | ||
var TRIM_TRAIL = /[ \t]+$/gm | ||
var | ||
RE_HASEXPR = /\x01#\d/, | ||
RE_REPEXPR = /\x01#(\d+)/g, | ||
RE_HASEXPR = safeRegex(/@#\d/, 'x01'), | ||
RE_REPEXPR = safeRegex(/@#(\d+)/g, 'x01'), | ||
CH_IDEXPR = '\x01#', | ||
@@ -229,3 +278,18 @@ CH_DQCODE = '\u2057', | ||
function compileImports (js) { | ||
var imp = [] | ||
var imports = '' | ||
while (imp = IMPORT_STATEMENT.exec(js)) { | ||
imports += imp[0].trim() + '\n' | ||
} | ||
return imports | ||
} | ||
function rmImports (js) { | ||
var jsCode = js.replace(IMPORT_STATEMENT, '') | ||
return jsCode | ||
} | ||
function _compileHTML (html, opts, pcex) { | ||
if (!/\S/.test(html)) return '' | ||
@@ -340,7 +404,4 @@ html = splitHtml(html, opts, pcex) | ||
var parser = opts.parser || (type ? parsers.js[type] : riotjs) | ||
var parser = opts.parser || type && parsers._req('js.' + type, true) || riotjs | ||
if (!parser) { | ||
throw new Error('JS parser not found: "' + type + '"') | ||
} | ||
return parser(js, parserOpts, url).replace(/\r\n?/g, '\n').replace(TRIM_TRAIL, '') | ||
@@ -401,6 +462,6 @@ } | ||
scoped = true | ||
} else if (parsers.css[type]) { | ||
css = parsers.css[type](tag, css, opts.parserOpts || {}, opts.url) | ||
} else if (type !== 'css') { | ||
throw new Error('CSS parser not found: "' + type + '"') | ||
var parser = parsers._req('css.' + type, true) | ||
css = parser(tag, css, opts.parserOpts || {}, opts.url) | ||
} | ||
@@ -441,3 +502,3 @@ } | ||
function mktag (name, html, css, attr, js, opts) { | ||
function mktag (name, html, css, attr, js, imports, opts) { | ||
var | ||
@@ -449,3 +510,3 @@ c = opts.debug ? ',\n ' : ', ', | ||
return 'riot.tag2(\'' + name + SQ + | ||
return imports + 'riot.tag2(\'' + name + SQ + | ||
c + _q(html, 1) + | ||
@@ -467,3 +528,5 @@ c + _q(css) + | ||
k += m.index + m[0].length | ||
return [str.slice(0, k), str.slice(k)] | ||
m = str.slice(0, k) | ||
if (m.slice(-5) === '<-/>\n') m = m.slice(0, -5) | ||
return [m, str.slice(k)] | ||
} | ||
@@ -503,7 +566,7 @@ n = k | ||
return str | ||
.replace('&', /&/g) | ||
.replace('<', /</g) | ||
.replace('>', />/g) | ||
.replace('"', /"/g) | ||
.replace(''', /'/g) | ||
.replace(/&/g, '&') | ||
.replace(/</g, '<') | ||
.replace(/>/g, '>') | ||
.replace(/"/g, '"') | ||
.replace(/'/g, '\'') | ||
} | ||
@@ -520,14 +583,24 @@ | ||
type = getType(attribs), | ||
src = getAttrib(attribs, 'src') | ||
src = getAttrib(attribs, 'src'), | ||
jsParserOptions = extend({}, opts.parserOptions.js) | ||
if (src) return false | ||
return _compileJS(code, opts, type, getParserOptions(attribs), base) | ||
return _compileJS( | ||
code, | ||
opts, | ||
type, | ||
extend(jsParserOptions, getParserOptions(attribs)), | ||
base | ||
) | ||
} | ||
function cssCode (code, opts, attribs, url, tag) { | ||
var extraOpts = { | ||
parserOpts: getParserOptions(attribs), | ||
scoped: attribs && /\sscoped(\s|=|$)/i.test(attribs), | ||
url: url | ||
} | ||
var | ||
parserStyleOptions = extend({}, opts.parserOptions.style), | ||
extraOpts = { | ||
parserOpts: extend(parserStyleOptions, getParserOptions(attribs)), | ||
scoped: attribs && /\sscoped(\s|=|$)/i.test(attribs), | ||
url: url | ||
} | ||
@@ -538,7 +611,4 @@ return _compileCSS(code, tag, getType(attribs) || opts.style, extraOpts) | ||
function compileTemplate (html, url, lang, opts) { | ||
var parser = parsers.html[lang] | ||
if (!parser) { | ||
throw new Error('Template parser not found: "' + lang + '"') | ||
} | ||
var parser = parsers._req('html.' + lang, true) | ||
return parser(html, opts, url) | ||
@@ -559,6 +629,14 @@ } | ||
parts = [], | ||
included | ||
included, | ||
defaultParserptions = { | ||
template: {}, | ||
js: {}, | ||
style: {} | ||
} | ||
if (!opts) opts = {} | ||
opts.parserOptions = extend(defaultParserptions, opts.parserOptions || {}) | ||
included = opts.exclude | ||
@@ -572,3 +650,3 @@ ? function (s) { return opts.exclude.indexOf(s) < 0 } : function () { return 1 } | ||
if (opts.template) { | ||
src = compileTemplate(src, url, opts.template, opts.templateOptions) | ||
src = compileTemplate(src, url, opts.template, opts.parserOptions.template) | ||
} | ||
@@ -582,2 +660,3 @@ | ||
html = '', | ||
imports = '', | ||
pcex = [] | ||
@@ -629,3 +708,9 @@ | ||
body = _compileJS(blocks[1], opts, null, null, url) | ||
imports = compileImports(jscode) | ||
jscode = rmImports(jscode) | ||
if (body) jscode += (jscode ? '\n' : '') + body | ||
jscode = jscode.replace(IMPORT_STATEMENT, function (s) { | ||
imports += s.trim() + '\n' | ||
return '' | ||
}) | ||
} | ||
@@ -643,3 +728,4 @@ } | ||
attribs: attribs, | ||
js: jscode | ||
js: jscode, | ||
imports: imports | ||
}) | ||
@@ -649,3 +735,3 @@ return '' | ||
return mktag(tagName, html, styles, attribs, jscode, opts) | ||
return mktag(tagName, html, styles, attribs, jscode, imports, opts) | ||
}) | ||
@@ -663,3 +749,3 @@ | ||
js: compileJS, | ||
version: 'v3.0.0-alpha.1' | ||
version: 'v3.0.0-alpha.2' | ||
} | ||
@@ -666,0 +752,0 @@ return compile |
@@ -67,3 +67,27 @@ # Compiler Guide (complement, WIP) | ||
In the above case, you can use this: | ||
```html | ||
<my-tag>I'm html</my-tag> | ||
``` | ||
But there's other cases... | ||
```html | ||
<my-title> | ||
{title} | ||
this.title = 'Title' | ||
<my-title> | ||
``` | ||
Here, the compiler cannot guess if `{title}` is an expression or a literal ES6 object. | ||
However, from v2.5.4 you can use the BuGless-hack... | ||
```html | ||
<my-title> | ||
{title}<-/> | ||
this.title = 'Title' | ||
</my-title> | ||
``` | ||
The ending `<-/>` puts `{title}` as expression in the html part and the `<-/>` is removed. | ||
### Whitespace | ||
@@ -154,3 +178,3 @@ | ||
| 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 (no preprocess) | ||
| type | js | string | JavaScript pre-processor. Built-in support for: es6, babel, buble, coffeescript, typescript, livescript, none (no preprocess) | ||
| style | css | string | CSS pre-processor. Built-in support for: sass, scss, less, stylus (only less in browsers) | ||
@@ -165,2 +189,4 @@ | entities | compile | boolean | Split the tag in its raw parts. | ||
From v2.5.4, `entities` includes the `import` declarations as a multiline string. | ||
Example: | ||
@@ -174,2 +200,3 @@ ```html | ||
<p/> | ||
import * as foo from "./module" | ||
click(e) { | ||
@@ -188,3 +215,3 @@ } | ||
{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);' } | ||
{tagName: 'tag2', html: '<p></p>', css: '#id1 {top:0}', attribs: '', js: ' this.click = function(e) {\n }.bind(this);', imports: 'import * as foo from "./module"\n' } | ||
] | ||
@@ -261,2 +288,12 @@ ``` | ||
### ES6 modules | ||
From v2.5.4 the riot-compiler offers limited support for ES6 modules (`import` declarations are hoisted and each must be written in one line). | ||
Also, the `entities` option returns the declarations as one multiline string. | ||
**Note:** An `import` declaration must not spawn multiple lines. | ||
See [Chapter 16 Modules](http://exploringjs.com/es6/ch_modules.html) of Exploring ES6. | ||
### Multiple JavaScript Blocks | ||
@@ -277,3 +314,3 @@ | ||
If you just want to get the `script` tag rendered, keeping the `type` | ||
attribute and the tag contents untouched, you should then use the `defer` | ||
attribute and the tag contents untouched, you should then use the `defer` | ||
attribute. | ||
@@ -399,5 +436,5 @@ | ||
var opts = {url: url}, | ||
css = compiler.js(code, 'babel', opts) | ||
css = compiler.js(code, 'buble', opts) | ||
``` | ||
will run `parsers.js.babel(code, opts.parserOpts, opts.url)` (inside the parser, the url will be passed as `{filename: url}` to babel). | ||
will run `parsers.js.buble(code, opts.parserOpts, opts.url)` (inside the parser, the url will be passed as `{source: url}` to bublé). | ||
@@ -404,0 +441,0 @@ **Note:** |
@@ -162,2 +162,3 @@ # Compiler | ||
- `babel` - (using `babel-core` v6.x and the `es2015` preset) | ||
- `buble` | ||
- `coffee` or `coffeescript` | ||
@@ -164,0 +165,0 @@ |
@@ -0,1 +1,3 @@ | ||
'use strict' | ||
/** | ||
@@ -5,2 +7,3 @@ * Brackets support for the node.js version of the riot-compiler | ||
*/ | ||
var safeRegex = require('./safe-regex.js') | ||
@@ -218,5 +221,4 @@ /** | ||
var | ||
INVALIDCH = /[\x00-\x1F<>a-zA-Z0-9'",;\\]/, // invalid characters for brackets | ||
ESCAPEDCH = /(?=[[\]()*+?.^$|])/g // this characters must be escaped | ||
var INVALIDCH = safeRegex(/[@-@<>a-zA-Z0-9'",;\\]/, 'x00', 'x1F') // invalid characters for brackets | ||
var ESCAPEDCH = /(?=[[\]()*+?.^$|])/g // this characters must be escaped | ||
@@ -223,0 +225,0 @@ /** |
/** | ||
* The riot-compiler v3.0.0-alpha.1 | ||
* The riot-compiler v3.0.0-alpha.2 | ||
* | ||
* @module compiler | ||
* @version v3.0.0-alpha.1 | ||
* @version v3.0.0-alpha.2 | ||
* @license MIT | ||
* @copyright 2015 Muut Inc. + contributors | ||
* @copyright Muut Inc. + contributors | ||
*/ | ||
'use strict' | ||
var brackets = require('./brackets') | ||
var parsers = require('./parsers') | ||
var path = require('path') | ||
var brackets = require('./brackets') | ||
var parsers = require('./parsers') | ||
var safeRegex = require('./safe-regex') | ||
var path = require('path') | ||
var extend = require('./parsers/_utils').mixobj | ||
/* eslint-enable */ | ||
/** | ||
@@ -101,2 +105,8 @@ * Source for creating regexes matching valid quoted, single-line JavaScript strings. | ||
/** | ||
* Matches the 'import' statement | ||
* @const {RegExp} | ||
*/ | ||
var IMPORT_STATEMENT = /^\s*import(?:\s*[*{]|\s+[$_a-zA-Z'"]).*\n?/gm | ||
/** | ||
* Matches trailing spaces and tabs by line. | ||
@@ -108,4 +118,4 @@ * @const {RegExp} | ||
var | ||
RE_HASEXPR = /\x01#\d/, | ||
RE_REPEXPR = /\x01#(\d+)/g, | ||
RE_HASEXPR = safeRegex(/@#\d/, 'x01'), | ||
RE_REPEXPR = safeRegex(/@#(\d+)/g, 'x01'), | ||
CH_IDEXPR = '\x01#', | ||
@@ -251,2 +261,26 @@ CH_DQCODE = '\u2057', | ||
/** | ||
* Return imports statement of the code as a string | ||
* @param {string} js - The js code containing the imports statement | ||
* @returns {string} Js code containing only the imports statement | ||
*/ | ||
function compileImports (js) { | ||
var imp = [] | ||
var imports = '' | ||
while (imp = IMPORT_STATEMENT.exec(js)) { | ||
imports += imp[0].trim() + '\n' | ||
} | ||
return imports | ||
} | ||
/** | ||
* Remove 'import' statement from JSCode | ||
* @param {string} js - The Js code | ||
* @returns {string} jsCode The js code without 'import' statement | ||
*/ | ||
function rmImports (js) { | ||
var jsCode = js.replace(IMPORT_STATEMENT, '') | ||
return jsCode | ||
} | ||
/** | ||
* The internal HTML compiler. | ||
@@ -262,2 +296,3 @@ * | ||
function _compileHTML (html, opts, pcex) { | ||
if (!/\S/.test(html)) return '' | ||
@@ -426,7 +461,4 @@ html = splitHtml(html, opts, pcex) | ||
var parser = opts.parser || (type ? parsers.js[type] : riotjs) | ||
var parser = opts.parser || type && parsers._req('js.' + type, true) || riotjs | ||
if (!parser) { | ||
throw new Error('JS parser not found: "' + type + '"') | ||
} | ||
return parser(js, parserOpts, url).replace(/\r\n?/g, '\n').replace(TRIM_TRAIL, '') | ||
@@ -530,6 +562,6 @@ } | ||
scoped = true | ||
} else if (parsers.css[type]) { | ||
css = parsers.css[type](tag, css, opts.parserOpts || {}, opts.url) | ||
} else if (type !== 'css') { | ||
throw new Error('CSS parser not found: "' + type + '"') | ||
var parser = parsers._req('css.' + type, true) | ||
css = parser(tag, css, opts.parserOpts || {}, opts.url) | ||
} | ||
@@ -647,6 +679,7 @@ } | ||
* @param {string} js - JavaScript "constructor" | ||
* @param {string} imports - Code containing 'import' statements | ||
* @param {object} opts - Compiler options | ||
* @returns {string} Code to call `riot.tag2` | ||
*/ | ||
function mktag (name, html, css, attr, js, opts) { | ||
function mktag (name, html, css, attr, js, imports, opts) { | ||
var | ||
@@ -658,3 +691,3 @@ c = opts.debug ? ',\n ' : ', ', | ||
return 'riot.tag2(\'' + name + SQ + | ||
return imports + 'riot.tag2(\'' + name + SQ + | ||
c + _q(html, 1) + | ||
@@ -684,3 +717,5 @@ c + _q(css) + | ||
k += m.index + m[0].length | ||
return [str.slice(0, k), str.slice(k)] | ||
m = str.slice(0, k) | ||
if (m.slice(-5) === '<-/>\n') m = m.slice(0, -5) | ||
return [m, str.slice(k)] | ||
} | ||
@@ -738,7 +773,7 @@ n = k | ||
return str | ||
.replace('&', /&/g) | ||
.replace('<', /</g) | ||
.replace('>', />/g) | ||
.replace('"', /"/g) | ||
.replace(''', /'/g) | ||
.replace(/&/g, '&') | ||
.replace(/</g, '<') | ||
.replace(/>/g, '>') | ||
.replace(/"/g, '"') | ||
.replace(/'/g, '\'') | ||
} | ||
@@ -772,3 +807,4 @@ | ||
type = getType(attribs), | ||
src = getAttrib(attribs, 'src') | ||
src = getAttrib(attribs, 'src'), | ||
jsParserOptions = extend({}, opts.parserOptions.js) | ||
@@ -783,3 +819,10 @@ if (src) { | ||
} | ||
return _compileJS(code, opts, type, getParserOptions(attribs), base) | ||
return _compileJS( | ||
code, | ||
opts, | ||
type, | ||
extend(jsParserOptions, getParserOptions(attribs)), | ||
base | ||
) | ||
} | ||
@@ -798,7 +841,9 @@ | ||
function cssCode (code, opts, attribs, url, tag) { | ||
var extraOpts = { | ||
parserOpts: getParserOptions(attribs), | ||
scoped: attribs && /\sscoped(\s|=|$)/i.test(attribs), | ||
url: url | ||
} | ||
var | ||
parserStyleOptions = extend({}, opts.parserOptions.style), | ||
extraOpts = { | ||
parserOpts: extend(parserStyleOptions, getParserOptions(attribs)), | ||
scoped: attribs && /\sscoped(\s|=|$)/i.test(attribs), | ||
url: url | ||
} | ||
@@ -820,7 +865,4 @@ return _compileCSS(code, tag, getType(attribs) || opts.style, extraOpts) | ||
function compileTemplate (html, url, lang, opts) { | ||
var parser = parsers.html[lang] | ||
if (!parser) { | ||
throw new Error('Template parser not found: "' + lang + '"') | ||
} | ||
var parser = parsers._req('html.' + lang, true) | ||
return parser(html, opts, url) | ||
@@ -883,6 +925,14 @@ } | ||
parts = [], | ||
included | ||
included, | ||
defaultParserptions = { | ||
template: {}, | ||
js: {}, | ||
style: {} | ||
} | ||
if (!opts) opts = {} | ||
opts.parserOptions = extend(defaultParserptions, opts.parserOptions || {}) | ||
included = opts.exclude | ||
@@ -896,3 +946,3 @@ ? function (s) { return opts.exclude.indexOf(s) < 0 } : function () { return 1 } | ||
if (opts.template) { | ||
src = compileTemplate(src, url, opts.template, opts.templateOptions) | ||
src = compileTemplate(src, url, opts.template, opts.parserOptions.template) | ||
} | ||
@@ -906,2 +956,3 @@ | ||
html = '', | ||
imports = '', | ||
pcex = [] | ||
@@ -954,3 +1005,9 @@ | ||
body = _compileJS(blocks[1], opts, null, null, url) | ||
imports = compileImports(jscode) | ||
jscode = rmImports(jscode) | ||
if (body) jscode += (jscode ? '\n' : '') + body | ||
jscode = jscode.replace(IMPORT_STATEMENT, function (s) { | ||
imports += s.trim() + '\n' | ||
return '' | ||
}) | ||
} | ||
@@ -968,3 +1025,4 @@ } | ||
attribs: attribs, | ||
js: jscode | ||
js: jscode, | ||
imports: imports | ||
}) | ||
@@ -974,3 +1032,3 @@ return '' | ||
return mktag(tagName, html, styles, attribs, jscode, opts) | ||
return mktag(tagName, html, styles, attribs, jscode, imports, opts) | ||
}) | ||
@@ -994,3 +1052,3 @@ | ||
parsers: parsers, | ||
version: 'v3.0.0-alpha.1' | ||
version: 'v3.0.0-alpha.2' | ||
} |
@@ -5,3 +5,7 @@ /** | ||
*/ | ||
'use strict' | ||
var REQPATH = './parsers/' | ||
var TRUE = true | ||
var NULL = null | ||
@@ -13,24 +17,117 @@ // Passtrough for the internal `none` and `javascript` parsers | ||
// Initialize the cache with parsers that cannot be required | ||
var _mods = { none: _none, javascript: _none } | ||
// This is the main parsers object holding the html, js, and css keys | ||
// initialized with the parsers that cannot be required. | ||
// | ||
var _parsers = { | ||
html: {}, | ||
css: {}, | ||
js: { none: _none, javascript: _none } | ||
} | ||
// Native riot parsers go here, having false if already required. | ||
var _loaders = { | ||
html: { jade: TRUE, pug: TRUE }, | ||
css: { sass: TRUE, scss: TRUE, less: TRUE, stylus: TRUE }, | ||
js: { es6: TRUE, babel: TRUE, buble: TRUE, coffee: TRUE, livescript: TRUE, typescript: TRUE } | ||
} | ||
_loaders.js.coffeescript = TRUE // 4 the nostalgics | ||
/** | ||
* Loads a "native" riot parser. | ||
* | ||
* It set the flag in the _loaders object to false for the required parser. | ||
* Try to load the parser and save the module to the _parsers object. | ||
* On error, throws a custom exception (adds 'riot' notice to the original). | ||
* On success returns the loaded module. | ||
* | ||
* @param {string} branch - The branch name inside _parsers/loaders | ||
* @param {string} parser - The parser's name | ||
* @returns {Function} Loaded module. | ||
*/ | ||
function _load (branch, parser) { | ||
var req = REQPATH + (parser === 'coffeescript' ? 'coffee' : parser) | ||
var mod | ||
_loaders[branch][parser] = false // try once | ||
_parsers[branch][parser] = null | ||
try { | ||
mod = _parsers[branch][parser] = require(req) | ||
} catch (e) { | ||
// istanbul ignore next | ||
var err = 'Can\'t load the ' + branch + '.' + parser + | ||
' riot parser: ' + ('' + e).replace(/^Error:\s/, '') | ||
// istanbul ignore next | ||
throw new Error(err) | ||
} | ||
return mod | ||
} | ||
/** | ||
* Returns the branch where the parser resides, or NULL if the parser not found. | ||
* If the parameter 'branch' is empty, the precedence order is js, css, html. | ||
* | ||
* @param {string} branch - The name of the branch to search, can be empty | ||
* @param {string} name - The parser's name | ||
* @returns {string} Name of the parser branch. | ||
*/ | ||
function _find (branch, name) { | ||
return branch ? _parsers[branch][name] && branch | ||
: _parsers.js[name] ? 'js' | ||
: _parsers.css[name] ? 'css' | ||
: _parsers.html[name] ? 'html' : NULL | ||
} | ||
/** | ||
* Returns a parser instance by its name, requiring the module without generating error. | ||
* Parsers name can include the branch (ej. 'js.es6'). | ||
* If branch is not included, the precedence order for searching is 'js', 'css', 'html' | ||
* | ||
* Public through the `parsers._req` function. | ||
* | ||
* @param {string} name - The parser's name, as registered in the parsers object | ||
* @param {string} [req] - To be used by require(). Defaults to parser's name | ||
* @returns {Function} The parser instance, null if the parser is not found. | ||
* @param {string} name - The parser's name, as registered in the parsers object | ||
* @param {boolean} [req] - true if required (throws on error) | ||
* @returns {Function} The parser instance, null if the parser is not found. | ||
*/ | ||
function _req (name, req) { | ||
var mod | ||
var | ||
err, | ||
mod, | ||
branch, | ||
parser = name.split('.') | ||
if (name in _mods) { | ||
mod = _mods[name] | ||
if (parser.length > 1) { | ||
branch = parser[0] | ||
parser = parser[1] | ||
} else { | ||
if (!req) req = REQPATH + (name === 'coffeescript' ? 'coffee' : name) | ||
try { mod = require(req) } catch (_) {/**/} | ||
_mods[name] = mod || null | ||
branch = NULL | ||
parser = name | ||
} | ||
// is the parser registered? | ||
branch = _find(branch, parser) | ||
if (!branch) { | ||
if (req) { | ||
err = 'Riot parser "' + name + '" is not registered.' | ||
throw new Error(err) | ||
} | ||
return NULL | ||
} | ||
// parser registered, needs load? | ||
if (_loaders[branch][parser]) { | ||
if (req) { | ||
mod = _load(branch, parser) | ||
} else { | ||
try { | ||
mod = _load(branch, parser) | ||
} catch (_) { | ||
// istanbul ignore next | ||
mod = NULL | ||
} | ||
} | ||
} else { | ||
mod = _parsers[branch][parser] | ||
} | ||
return mod | ||
@@ -46,22 +143,21 @@ } | ||
*/ | ||
function _makelist (_p) { | ||
var names = { | ||
html: ['jade'], | ||
css: ['sass', 'scss', 'less', 'stylus'], | ||
js: ['es6', 'babel', 'coffee', 'livescript', 'typescript'] | ||
} | ||
function _setLoaders (_p) { | ||
// loads the module at first use and returns the parsed result | ||
function mkloader (dest, name) { | ||
function mkloader (branch, parser) { | ||
return function _loadParser (p1, p2, p3, p4) { | ||
return (dest[name] = _req(name))(p1, p2, p3, p4) | ||
var fn = _load(branch, parser) | ||
return fn(p1, p2, p3, p4) | ||
} | ||
} | ||
for (var type in names) { // eslint-disable-line guard-for-in | ||
var dest = _p[type] | ||
for (var branch in _loaders) { | ||
// istanbul ignore else | ||
if (_loaders.hasOwnProperty(branch)) { | ||
var names = Object.keys(_loaders[branch]) | ||
names[type].forEach(function (name) { // eslint-disable-line no-loop-func | ||
dest[name] = mkloader(dest, name) | ||
}) | ||
names.forEach(function (name) { | ||
_p[branch][name] = mkloader(branch, name) | ||
}) | ||
} | ||
} | ||
@@ -71,10 +167,4 @@ return _p | ||
// Exports the initialized parsers | ||
module.exports = _makelist({ | ||
_req: _req, | ||
html: {}, | ||
css: {}, | ||
js: { none: _none, javascript: _none } | ||
}) | ||
_setLoaders(_parsers)._req = _req | ||
module.exports.js.coffeescript = module.exports.js.coffee // 4 the nostalgics | ||
module.exports = _parsers |
@@ -11,4 +11,3 @@ /* | ||
mixobj = require('./_utils').mixobj, | ||
tryreq = require('./_utils').tryreq, | ||
getdir = require('path').dirname | ||
tryreq = require('./_utils').tryreq | ||
@@ -24,7 +23,7 @@ // istanbul ignore next: throws error if cannot load any | ||
module.exports = function _es6 (js, opts, url) { | ||
module.exports = function _es6 (js, opts) { | ||
opts = mixobj(defopts, { resolveModuleSource: getdir(url) }, opts) | ||
opts = mixobj(defopts, opts) | ||
return parser.transform(js, opts).code | ||
} |
@@ -18,2 +18,6 @@ /* | ||
/* eslint-disable */ | ||
console.log('DEPRECATION WARNING: jade was renamed "pug" - the jade parser will be removed in riot@3.0.0!') | ||
/* eslint-enable */ | ||
module.exports = function _jade (html, opts, url) { | ||
@@ -20,0 +24,0 @@ |
@@ -8,2 +8,3 @@ /* | ||
2016-03-09: Initital release | ||
2016-08-30: Fixed issues with indentation | ||
*/ | ||
@@ -22,3 +23,11 @@ var | ||
module.exports = function _sass (tag, css, opts, url) { | ||
var spc = css.match(/^\s+/) | ||
if (spc) { | ||
css = css.replace(RegExp('^' + spc[0], 'gm'), '') | ||
if (/^\t/gm.test(css)) { | ||
opts.indentType = 'tab' | ||
} | ||
} | ||
opts = mixobj(defopts, { data: css, includePaths: [getdir(url)] }, opts) | ||
@@ -25,0 +34,0 @@ |
{ | ||
"name": "riot-compiler", | ||
"version": "3.0.0-alpha.1", | ||
"version": "3.0.0-alpha.2", | ||
"description": "Compiler for riot .tag files", | ||
@@ -13,6 +13,4 @@ "main": "lib/compiler.js", | ||
"lib", | ||
"src", | ||
"doc", | ||
"dist/*.js", | ||
"test/**" | ||
"dist/*.js" | ||
], | ||
@@ -34,11 +32,18 @@ "scripts": { | ||
"devDependencies": { | ||
"coveralls": "^2.11.8", | ||
"eslint": "^2.2.0", | ||
"coveralls": "^2.11.14", | ||
"eslint": "^3.7.0", | ||
"expect.js": "^0.3.1", | ||
"istanbul": "^0.4.2", | ||
"istanbul": "^0.4.5", | ||
"jspreproc": "^0.2.7", | ||
"mocha": "^2.4.5", | ||
"mocha": "^3.1.0", | ||
"riot-bump": "^1.0.0" | ||
}, | ||
"author": "Muut, Inc. and other contributors", | ||
"author": "Riot maintainers team + smart people from all over the world", | ||
"contributors": [ | ||
"Richard Bondi https://github.com/rsbondi", | ||
"Gianluca Guarini https://github.com/GianlucaGuarini", | ||
"Tsutomu Kawamura https://github.com/cognitom", | ||
"Alberto Martínez https://github.com/aMarCruz", | ||
"Tero Piirainen https://github.com/tipiirai" | ||
], | ||
"license": "MIT", | ||
@@ -45,0 +50,0 @@ "bugs": { |
@@ -18,7 +18,2 @@ [![Build Status][travis-image]][travis-url] | ||
### Bower | ||
`$ bower install riot-compiler --save` | ||
### Read more in the [doc folder](doc/) and the [CHANGELOG](CHANGELOG.md) | ||
@@ -25,0 +20,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
3
126767
26
2693
45