first commit
This commit is contained in:
429
node_modules/@n8n_io/riot-tmpl/src/brackets.js
generated
vendored
Normal file
429
node_modules/@n8n_io/riot-tmpl/src/brackets.js
generated
vendored
Normal file
@@ -0,0 +1,429 @@
|
||||
/**
|
||||
* riot.util.brackets
|
||||
*
|
||||
* - `brackets ` - Returns a string or regex based on its parameter
|
||||
* - `brackets.set` - Change the current riot brackets
|
||||
*
|
||||
* @module
|
||||
*/
|
||||
//#if 0 // only in the unprocessed source
|
||||
/* eslint no-unused-vars: [2, {args: "after-used", varsIgnorePattern: "^brackets$"}] */
|
||||
/* global skipRegex */
|
||||
//#endif
|
||||
|
||||
//#if ES6
|
||||
/* global riot */
|
||||
|
||||
export
|
||||
//#endif
|
||||
var brackets = (function (UNDEF) {
|
||||
//
|
||||
// Closure data
|
||||
// --------------------------------------------------------------------------
|
||||
//
|
||||
//#set $_RIX_TEST = 4
|
||||
//#set $_RIX_ESC = 5
|
||||
//#set $_RIX_OPEN = 6
|
||||
//#set $_RIX_CLOSE = 7
|
||||
//#set $_RIX_PAIR = 8
|
||||
//#set $_RIX_LOOP = 9
|
||||
//#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,
|
||||
$_RIX_LOOP = 9
|
||||
//#endif
|
||||
|
||||
var
|
||||
REGLOB = 'g',
|
||||
|
||||
/**
|
||||
* Used by internal functions and shared with the riot compiler, matches valid,
|
||||
* multiline JavaScript comments in almost all its forms. Can handle embedded
|
||||
* sequences `/\*`, `*\/` and `//` inside these. Skips non-valid comments like `/*\/`
|
||||
*
|
||||
* `R_MLCOMMS` does not make captures.
|
||||
* @const {RegExp}
|
||||
* @static
|
||||
*/
|
||||
R_MLCOMMS = /\/\*[^*]*\*+(?:[^*\/][^*]*\*+)*\//g,
|
||||
|
||||
/**
|
||||
* Used by internal functions and shared with the riot compiler, matches single or
|
||||
* double quoted strings, handles embedded quotes and multi-line strings (not in
|
||||
* accordance with the JavaScript spec).
|
||||
* It is not for ES6 template strings.
|
||||
*
|
||||
* `R_STRINGS` does not make captures.
|
||||
* @const {RegExp}
|
||||
* @static
|
||||
*/
|
||||
R_STRINGS = /"[^"\\]*(?:\\[\S\s][^"\\]*)*"|'[^'\\]*(?:\\[\S\s][^'\\]*)*'|`[^`\\]*(?:\\[\S\s][^`\\]*)*`/g,
|
||||
|
||||
/**
|
||||
* For use with the RegExp constructor. Combines the
|
||||
* {@link module:brackets.R_STRINGS|R_STRINGS} source with sources of regexes matching
|
||||
* division operators and literal regexes.
|
||||
* The resulting regex captures in `$1` and `$2` a single slash, depending
|
||||
* if it matches a division operator ($1) or a literal regex ($2).
|
||||
* @const {string}
|
||||
* @static
|
||||
*/
|
||||
S_QBLOCKS = R_STRINGS.source + '|' +
|
||||
/(?:\breturn\s+|(?:[$\w\)\]]|\+\+|--)\s*(\/)(?![*\/]))/.source + '|' +
|
||||
/\/(?=[^*\/])[^[\/\\]*(?:(?:\[(?:\\.|[^\]\\]*)*\]|\\.)[^[\/\\]*)*?([^<]\/)[gim]*/.source,
|
||||
|
||||
/**
|
||||
* Characters not supported by the expression parser.
|
||||
* @const {RegExp}
|
||||
* @static
|
||||
*/
|
||||
UNSUPPORTED = RegExp('[\\' + 'x00-\\x1F<>a-zA-Z0-9\'",;\\\\]'),
|
||||
|
||||
/**
|
||||
* These characters have to be escaped - Note that '{}' is not in this list.
|
||||
* @const {RegExp}
|
||||
* @static
|
||||
*/
|
||||
NEED_ESCAPE = /(?=[[\]()*+?.^$|])/g,
|
||||
|
||||
/*
|
||||
JS/ES6 quoted strings and start of regex (basic ES6 does not supports nested backquotes).
|
||||
*/
|
||||
S_QBLOCK2 = R_STRINGS.source + '|' + /(\/)(?![*\/])/.source,
|
||||
|
||||
/**
|
||||
* Hash of regexes for matching JavaScript brackets out of quoted strings and literal
|
||||
* regexes. Used by {@link module:brackets.split|split}, these are heavy, but their
|
||||
* performance is acceptable.
|
||||
* @const {object}
|
||||
* @private
|
||||
*/
|
||||
FINDBRACES = {
|
||||
'(': RegExp('([()])|' + S_QBLOCK2, REGLOB),
|
||||
'[': RegExp('([[\\]])|' + S_QBLOCK2, REGLOB),
|
||||
'{': RegExp('([{}])|' + S_QBLOCK2, REGLOB)
|
||||
},
|
||||
|
||||
/**
|
||||
* The predefined riot brackets
|
||||
* @const {string}
|
||||
* @default
|
||||
*/
|
||||
DEFAULT = '{ }'
|
||||
|
||||
// pre-made string and regexes for the default brackets
|
||||
var _pairs = [
|
||||
'{', '}', // [0-1]: separated brackets
|
||||
'{', '}', // [2-3]: separated brackets (escaped)
|
||||
/{[^}]*}/, // $_RIX_TEST
|
||||
/\\([{}])/g, // $_RIX_ESC
|
||||
/\\({)|{/g, // $_RIX_OPEN
|
||||
RegExp('\\\\(})|([[({])|(})|' + S_QBLOCK2, REGLOB), // $_RIX_CLOSE
|
||||
DEFAULT, // $_RIX_PAIR
|
||||
/^\s*{\^?\s*([$\w]+)(?:\s*,\s*(\S+))?\s+in\s+(\S.*)\s*}/, // $_RIX_LOOP
|
||||
/(^|[^\\]){=[\S\s]*?}/ // $_RIX_RAW
|
||||
]
|
||||
|
||||
// Variable information about the current brackets state, initialized on first use
|
||||
// and on bracket changes.
|
||||
var
|
||||
cachedBrackets = UNDEF, // full brackets string in use, for change detection
|
||||
_regex, // function for regex convertion of default brackets
|
||||
_cache = [], // pre-made string and regexes for the current brackets
|
||||
_settings // mirror `riot.settings`
|
||||
|
||||
//
|
||||
// Private functions
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Rewrite function for default brackets, returns the received regex as-is.
|
||||
* Used by the main brackets function when the current brackets are the default.
|
||||
* @param {RegExp} re RegExp instance with the default riot brackets
|
||||
* @returns {RegExp} The received regex.
|
||||
* @private
|
||||
*/
|
||||
function _loopback (re) { return re }
|
||||
|
||||
/**
|
||||
* Rewrite the regex with the default brackets replaced with the custom ones.
|
||||
* Used by the main brackets function when the current brackets are not the default.
|
||||
* @param {RegExp} re - RegExp instance with the default riot brackets
|
||||
* @param {Array} [bp] - Escaped brackets in elements 2-3, defaults to those in _cache
|
||||
* @returns {RegExp} Copy of the received regex, with the default brackets replaced.
|
||||
* @private
|
||||
*/
|
||||
function _rewrite (re, bp) {
|
||||
if (!bp) bp = _cache
|
||||
return new RegExp(
|
||||
re.source.replace(/{/g, bp[2]).replace(/}/g, bp[3]), re.global ? REGLOB : ''
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an array with pre-made strings and regexes based on the received brackets.
|
||||
* With the default brackets, returns a reference to an inner static array.
|
||||
*
|
||||
* Does not accept `<, >, a-z, A-Z, 0-9` nor control characters.
|
||||
* @param {string} pair - String with the desired brackets pair (cannot be falsy)
|
||||
* @returns {Array} Array with information for the given brackets.
|
||||
* @throws Will throw an "Unsupported brackets ..." if _pair_ is not separated with
|
||||
* exactly one space, or contains an invalid character.
|
||||
* @private
|
||||
*/
|
||||
function _create (pair) {
|
||||
if (pair === DEFAULT) return _pairs
|
||||
|
||||
var arr = pair.split(' ')
|
||||
|
||||
if (arr.length !== 2 || UNSUPPORTED.test(pair)) {
|
||||
throw new Error('Unsupported brackets "' + pair + '"')
|
||||
}
|
||||
arr = arr.concat(pair.replace(NEED_ESCAPE, '\\').split(' '))
|
||||
|
||||
arr[$_RIX_TEST] = _rewrite(arr[1].length > 1 ? /{[\S\s]*?}/ : _pairs[$_RIX_TEST], arr)
|
||||
arr[$_RIX_ESC] = _rewrite(pair.length > 3 ? /\\({|})/g : _pairs[$_RIX_ESC], arr)
|
||||
arr[$_RIX_OPEN] = _rewrite(_pairs[$_RIX_OPEN], arr) // for _split()
|
||||
arr[$_RIX_CLOSE] = RegExp('\\\\(' + arr[3] + ')|([[({])|(' + arr[3] + ')|' + S_QBLOCK2, REGLOB)
|
||||
arr[$_RIX_PAIR] = pair
|
||||
return arr
|
||||
}
|
||||
|
||||
//
|
||||
// "Exported" functions
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* The main function.
|
||||
*
|
||||
* With a numeric parameter, returns the current left (0) or right (1) riot brackets.
|
||||
*
|
||||
* With a regex, returns the original regex if the current brackets are the defaults,
|
||||
* or a new one with the default brackets replaced by the current custom brackets.
|
||||
* @param {RegExp|number} reOrIdx - As noted above
|
||||
* @returns {RegExp|string} Based on the received parameter.
|
||||
* @alias brackets
|
||||
*/
|
||||
function _brackets (reOrIdx) {
|
||||
return reOrIdx instanceof RegExp ? _regex(reOrIdx) : _cache[reOrIdx]
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits the received string in its template text and expression parts using
|
||||
* balanced brackets detection to avoid require escaped brackets from users.
|
||||
*
|
||||
* _For internal use by tmpl and the riot-compiler._
|
||||
* @param {string} str - Template source to split, can be one expression
|
||||
* @param {number} [tmpl] - 1 if called from `tmpl()`
|
||||
* @param {Array} [_bp] - Info of custom brackets to use
|
||||
* @returns {Array} - Array containing template text and expressions.
|
||||
* If str was one unique expression, returns two elements: ["", expression].
|
||||
* @private
|
||||
*/
|
||||
_brackets.split = function split (str, tmpl, _bp) {
|
||||
// istanbul ignore next: _bp is for the compiler
|
||||
if (!_bp) _bp = _cache
|
||||
|
||||
// Template text is easy: closing brackets are ignored, all we have to do is find
|
||||
// the first unescaped bracket. The real work is with the expressions...
|
||||
//
|
||||
// Expressions are not so easy. We can already ignore opening brackets, but finding
|
||||
// the correct closing bracket is tricky.
|
||||
// Strings and regexes can contain almost any combination of characters and we
|
||||
// can't deal with these complexity with our regexes, so let's hide and ignore
|
||||
// these. From there, all we need is to detect the bracketed parts and skip
|
||||
// them, as they contains most of the common characters used by riot brackets.
|
||||
// With that, we have a 90% reliability in the detection, although (hope few) some
|
||||
// custom brackets still requires to be escaped.
|
||||
var
|
||||
parts = [], // holds the resulting parts
|
||||
match, // reused by both outer and nested searches
|
||||
isexpr, // we are in ttext (0) or expression (1)
|
||||
start, // start position of current template or expression
|
||||
pos, // current position (exec() result)
|
||||
re = _bp[$_RIX_OPEN] // start with *updated* opening bracket
|
||||
|
||||
var qblocks = [] // quoted strings and regexes
|
||||
var prevStr = ''
|
||||
var mark, lastIndex
|
||||
|
||||
isexpr = start = re.lastIndex = 0 // re is reused, we must reset lastIndex
|
||||
|
||||
while ((match = re.exec(str))) {
|
||||
|
||||
lastIndex = re.lastIndex
|
||||
pos = match.index
|
||||
|
||||
if (isexpr) {
|
||||
// $1: optional escape character,
|
||||
// $2: opening js bracket `{[(`,
|
||||
// $3: closing riot bracket,
|
||||
// $4: opening slashes of regex
|
||||
|
||||
if (match[2]) { // if have a javascript opening bracket,
|
||||
//re.lastIndex = skipBraces(str, match[2], re.lastIndex)
|
||||
var ch = match[2]
|
||||
var rech = FINDBRACES[ch]
|
||||
var ix = 1
|
||||
|
||||
rech.lastIndex = lastIndex
|
||||
while ((match = rech.exec(str))) {
|
||||
if (match[1]) {
|
||||
if (match[1] === ch) ++ix
|
||||
else if (!--ix) break
|
||||
} else {
|
||||
rech.lastIndex = pushQBlock(match.index, rech.lastIndex, match[2])
|
||||
}
|
||||
}
|
||||
re.lastIndex = ix ? str.length : rech.lastIndex
|
||||
continue // skip the bracketed block and loop
|
||||
}
|
||||
|
||||
if (!match[3]) { // if don't have a closing bracket
|
||||
re.lastIndex = pushQBlock(pos, lastIndex, match[4])
|
||||
continue // search again
|
||||
}
|
||||
}
|
||||
|
||||
// At this point, we expect an _unescaped_ openning bracket in $2 for text,
|
||||
// or a closing bracket in $3 for expression.
|
||||
|
||||
if (!match[1]) { // ignore it if have an escape char
|
||||
unescapeStr(str.slice(start, pos)) // push part, even if empty
|
||||
start = re.lastIndex // next position is the new start
|
||||
re = _bp[$_RIX_OPEN + (isexpr ^= 1)] // switch mode and swap regexp
|
||||
re.lastIndex = start // update the regex pointer
|
||||
}
|
||||
}
|
||||
|
||||
if (str && start < str.length) { // push remaining part, if we have one
|
||||
unescapeStr(str.slice(start))
|
||||
}
|
||||
|
||||
// send the literal strings as an array property
|
||||
parts.qblocks = qblocks
|
||||
|
||||
return parts
|
||||
|
||||
// Inner Helpers for _split() -----
|
||||
|
||||
// Store the string in the array `parts`.
|
||||
// Unescape escaped brackets from expressions and, if we are called from
|
||||
// tmpl, from the HTML part too.
|
||||
function unescapeStr (s) {
|
||||
if (prevStr) {
|
||||
s = prevStr + s
|
||||
prevStr = ''
|
||||
}
|
||||
if (tmpl || isexpr) {
|
||||
parts.push(s && s.replace(_bp[$_RIX_ESC], '$1'))
|
||||
} else {
|
||||
parts.push(s)
|
||||
}
|
||||
}
|
||||
|
||||
// Find the js closing bracket for the current block.
|
||||
// Skips strings, regexes, and other inner blocks.
|
||||
function pushQBlock(_pos, _lastIndex, slash) { //eslint-disable-line
|
||||
if (slash) {
|
||||
_lastIndex = skipRegex(str, _pos)
|
||||
}
|
||||
// do not save empty strings or non-regex slashes
|
||||
if (tmpl && _lastIndex > _pos + 2) {
|
||||
mark = '\u2057' + qblocks.length + '~'
|
||||
qblocks.push(str.slice(_pos, _lastIndex))
|
||||
prevStr += str.slice(start, _pos) + mark
|
||||
start = _lastIndex
|
||||
}
|
||||
return _lastIndex
|
||||
}
|
||||
}
|
||||
|
||||
// exposed by riot.util.tmpl.hasExpr
|
||||
_brackets.hasExpr = function hasExpr (str) {
|
||||
return _cache[$_RIX_TEST].test(str)
|
||||
}
|
||||
|
||||
// exposed by riot.util.tmpl.loopKeys
|
||||
_brackets.loopKeys = function loopKeys (expr) {
|
||||
var m = expr.match(_cache[$_RIX_LOOP])
|
||||
|
||||
return m
|
||||
? { key: m[1], pos: m[2], val: _cache[0] + m[3].trim() + _cache[1] }
|
||||
: { val: expr.trim() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array with brackets information, defaults to the current brackets.
|
||||
* (the `brackets` module in the node version of the compiler allways defaults
|
||||
* to the predefined riot brackets `{ }`).
|
||||
*
|
||||
* _This function is for internal use._
|
||||
* @param {string} [pair] - If used, returns info for this brackets
|
||||
* @returns {Array} Brackets array in internal format.
|
||||
* @private
|
||||
*/
|
||||
_brackets.array = function array (pair) {
|
||||
return pair ? _create(pair) : _cache
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the _cache array with strings and regexes based on its parameter.
|
||||
* @param {string} [pair=DEFAULT] - String with the brackets pair to set
|
||||
* @alias brackets.set
|
||||
*/
|
||||
function _reset (pair) {
|
||||
if ((pair || (pair = DEFAULT)) !== _cache[$_RIX_PAIR]) {
|
||||
_cache = _create(pair)
|
||||
_regex = pair === DEFAULT ? _loopback : _rewrite
|
||||
_cache[$_RIX_LOOP] = _regex(_pairs[$_RIX_LOOP])
|
||||
}
|
||||
cachedBrackets = pair // always set these
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows change detection of `riot.settings.brackets`.
|
||||
* @param {object} o - Where store the `brackets` property, mostly `riot.settings`
|
||||
* @private
|
||||
*/
|
||||
function _setSettings (o) {
|
||||
var b
|
||||
|
||||
o = o || {}
|
||||
b = o.brackets
|
||||
Object.defineProperty(o, 'brackets', {
|
||||
set: _reset,
|
||||
get: function () { return cachedBrackets },
|
||||
enumerable: true
|
||||
})
|
||||
_settings = o // save the new reference
|
||||
_reset(b) // update the brackets
|
||||
}
|
||||
|
||||
// Inmediate execution
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
// Set the internal _settings property as reference to `riot.settings`
|
||||
Object.defineProperty(_brackets, 'settings', {
|
||||
set: _setSettings,
|
||||
get: function () { return _settings }
|
||||
})
|
||||
|
||||
/* istanbul ignore next: in the browser riot is always in the scope */
|
||||
_brackets.settings = typeof riot !== 'undefined' && riot.settings || {}
|
||||
_brackets.set = _reset
|
||||
_brackets.skipRegex = skipRegex
|
||||
|
||||
// Public properties, shared with `tmpl`
|
||||
_brackets.R_STRINGS = R_STRINGS
|
||||
_brackets.R_MLCOMMS = R_MLCOMMS
|
||||
_brackets.S_QBLOCKS = S_QBLOCKS
|
||||
_brackets.S_QBLOCK2 = S_QBLOCK2
|
||||
|
||||
return _brackets
|
||||
|
||||
})()
|
||||
50
node_modules/@n8n_io/riot-tmpl/src/index.js
generated
vendored
Normal file
50
node_modules/@n8n_io/riot-tmpl/src/index.js
generated
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
//#if CSP
|
||||
// note that the path must be relative to the dist/ folder
|
||||
import safeEval from '../src/notevil' //eslint-disable-line no-unused-vars
|
||||
//#endif
|
||||
|
||||
//#if 0
|
||||
/* global tmpl, brackets, window */
|
||||
/* eslint-disable no-void */
|
||||
/* eslint-env amd */
|
||||
//#endif
|
||||
//#if NODE
|
||||
/* riot-tmpl WIP, @license MIT, (c) 2015 Muut Inc. + contributors */
|
||||
;(function (window) { // eslint-disable-line no-extra-semi
|
||||
'use strict'
|
||||
//#else
|
||||
|
||||
/**
|
||||
* The riot template engine
|
||||
* @version WIP
|
||||
*/
|
||||
//#endif
|
||||
|
||||
//#include skip-regex
|
||||
|
||||
//#include brackets
|
||||
|
||||
//#include tmpl
|
||||
|
||||
//#if NODE
|
||||
tmpl.version = brackets.version = 'WIP'
|
||||
|
||||
// support CommonJS, AMD & browser
|
||||
/* istanbul ignore else */
|
||||
if (typeof module === 'object' && module.exports) {
|
||||
module.exports = {
|
||||
tmpl: tmpl, brackets: brackets
|
||||
}
|
||||
} else if (typeof define === 'function' && typeof define.amd !== 'undefined') {
|
||||
define(function () {
|
||||
return {
|
||||
tmpl: tmpl, brackets: brackets
|
||||
}
|
||||
})
|
||||
} else if (window) {
|
||||
window.tmpl = tmpl
|
||||
window.brackets = brackets
|
||||
}
|
||||
|
||||
})(typeof window === 'object' ? /* istanbul ignore next */ window : void 0)
|
||||
//#endif
|
||||
4
node_modules/@n8n_io/riot-tmpl/src/notevil/helpers.js
generated
vendored
Normal file
4
node_modules/@n8n_io/riot-tmpl/src/notevil/helpers.js
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
export function getGlobal (str) {
|
||||
var ctx = (typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : this)
|
||||
return typeof str !== 'undefined' ? ctx[str] : ctx
|
||||
}
|
||||
527
node_modules/@n8n_io/riot-tmpl/src/notevil/index.js
generated
vendored
Normal file
527
node_modules/@n8n_io/riot-tmpl/src/notevil/index.js
generated
vendored
Normal file
@@ -0,0 +1,527 @@
|
||||
import InfiniteChecker from './lib/infinite-checker'
|
||||
import Primitives from './lib/primitives'
|
||||
import { getGlobal } from './helpers'
|
||||
import esprima from 'esprima'
|
||||
import hoist from 'hoister'
|
||||
|
||||
var maxIterations = 1000000,
|
||||
parse = esprima.parse
|
||||
|
||||
// 'eval' with a controlled environment
|
||||
function safeEval(src, parentContext){
|
||||
var tree = prepareAst(src)
|
||||
var context = Object.create(parentContext || {})
|
||||
return finalValue(evaluateAst(tree, context))
|
||||
}
|
||||
|
||||
safeEval.func = FunctionFactory()
|
||||
|
||||
// create a 'Function' constructor for a controlled environment
|
||||
function FunctionFactory(parentContext){
|
||||
var context = Object.create(parentContext || {})
|
||||
return function Function() {
|
||||
// normalize arguments array
|
||||
var args = Array.prototype.slice.call(arguments)
|
||||
var src = args.slice(-1)[0]
|
||||
args = args.slice(0,-1)
|
||||
if (typeof src === 'string'){
|
||||
//HACK: esprima doesn't like returns outside functions
|
||||
src = parse('function a(){ ' + src + '}').body[0].body
|
||||
}
|
||||
var tree = prepareAst(src)
|
||||
return getFunction(tree, args, context)
|
||||
}
|
||||
}
|
||||
|
||||
// takes an AST or js source and returns an AST
|
||||
function prepareAst(src){
|
||||
var tree = (typeof src === 'string') ? parse(src) : src
|
||||
return hoist(tree)
|
||||
}
|
||||
|
||||
// evaluate an AST in the given context
|
||||
function evaluateAst(tree, context){
|
||||
|
||||
var safeFunction = FunctionFactory(context)
|
||||
var primitives = Primitives(context)
|
||||
|
||||
// block scoped context for catch (ex) and 'let'
|
||||
var blockContext = context
|
||||
|
||||
return walk(tree)
|
||||
|
||||
// recursively walk every node in an array
|
||||
function walkAll(nodes){
|
||||
var result = undefined
|
||||
for (var i=0;i<nodes.length;i++){
|
||||
var childNode = nodes[i]
|
||||
if (childNode.type === 'EmptyStatement') continue
|
||||
result = walk(childNode)
|
||||
if (result instanceof ReturnValue){
|
||||
return result
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// recursively evalutate the node of an AST
|
||||
function walk(node){
|
||||
if (!node) return
|
||||
|
||||
switch (node.type) {
|
||||
|
||||
case 'Program':
|
||||
return walkAll(node.body )
|
||||
|
||||
case 'BlockStatement':
|
||||
enterBlock()
|
||||
var result = walkAll(node.body)
|
||||
leaveBlock()
|
||||
return result
|
||||
|
||||
case 'SequenceExpression':
|
||||
return walkAll(node.expressions)
|
||||
|
||||
case 'FunctionDeclaration':
|
||||
var params = node.params.map(getName)
|
||||
var value = getFunction(node.body, params, blockContext)
|
||||
return context[node.id.name] = value
|
||||
|
||||
case 'FunctionExpression':
|
||||
var params = node.params.map(getName)
|
||||
return getFunction(node.body, params, blockContext)
|
||||
|
||||
case 'ReturnStatement':
|
||||
var value = walk(node.argument)
|
||||
return new ReturnValue('return', value)
|
||||
|
||||
case 'BreakStatement':
|
||||
return new ReturnValue('break')
|
||||
|
||||
case 'ContinueStatement':
|
||||
return new ReturnValue('continue')
|
||||
|
||||
case 'ExpressionStatement':
|
||||
return walk(node.expression)
|
||||
|
||||
case 'AssignmentExpression':
|
||||
return setValue(blockContext, node.left, node.right, node.operator)
|
||||
|
||||
case 'UpdateExpression':
|
||||
return setValue(blockContext, node.argument, null, node.operator)
|
||||
|
||||
case 'VariableDeclaration':
|
||||
node.declarations.forEach(function(declaration){
|
||||
var target = node.kind === 'let' ? blockContext : context
|
||||
if (declaration.init){
|
||||
target[declaration.id.name] = walk(declaration.init)
|
||||
} else {
|
||||
target[declaration.id.name] = undefined
|
||||
}
|
||||
})
|
||||
break
|
||||
|
||||
case 'SwitchStatement':
|
||||
var defaultHandler = null
|
||||
var matched = false
|
||||
var value = walk(node.discriminant)
|
||||
var result = undefined
|
||||
|
||||
enterBlock()
|
||||
|
||||
var i = 0
|
||||
while (result == null){
|
||||
if (i<node.cases.length){
|
||||
if (node.cases[i].test){ // check or fall through
|
||||
matched = matched || (walk(node.cases[i].test) === value)
|
||||
} else if (defaultHandler == null) {
|
||||
defaultHandler = i
|
||||
}
|
||||
if (matched){
|
||||
var r = walkAll(node.cases[i].consequent)
|
||||
if (r instanceof ReturnValue){ // break out
|
||||
if (r.type == 'break') break
|
||||
result = r
|
||||
}
|
||||
}
|
||||
i += 1 // continue
|
||||
} else if (!matched && defaultHandler != null){
|
||||
// go back and do the default handler
|
||||
i = defaultHandler
|
||||
matched = true
|
||||
} else {
|
||||
// nothing we can do
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
leaveBlock()
|
||||
return result
|
||||
|
||||
case 'IfStatement':
|
||||
if (walk(node.test)){
|
||||
return walk(node.consequent)
|
||||
} else if (node.alternate) {
|
||||
return walk(node.alternate)
|
||||
}
|
||||
|
||||
case 'ForStatement':
|
||||
var infinite = InfiniteChecker(maxIterations)
|
||||
var result = undefined
|
||||
|
||||
enterBlock() // allow lets on delarations
|
||||
for (walk(node.init); walk(node.test); walk(node.update)){
|
||||
var r = walk(node.body)
|
||||
|
||||
// handle early return, continue and break
|
||||
if (r instanceof ReturnValue){
|
||||
if (r.type == 'continue') continue
|
||||
if (r.type == 'break') break
|
||||
result = r
|
||||
break
|
||||
}
|
||||
|
||||
infinite.check()
|
||||
}
|
||||
leaveBlock()
|
||||
return result
|
||||
|
||||
case 'ForInStatement':
|
||||
var infinite = InfiniteChecker(maxIterations)
|
||||
var result = undefined
|
||||
|
||||
var value = walk(node.right)
|
||||
var property = node.left
|
||||
|
||||
var target = context
|
||||
enterBlock()
|
||||
|
||||
if (property.type == 'VariableDeclaration'){
|
||||
walk(property)
|
||||
property = property.declarations[0].id
|
||||
if (property.kind === 'let'){
|
||||
target = blockContext
|
||||
}
|
||||
}
|
||||
|
||||
for (var key in value){
|
||||
setValue(target, property, {type: 'Literal', value: key})
|
||||
var r = walk(node.body)
|
||||
|
||||
// handle early return, continue and break
|
||||
if (r instanceof ReturnValue){
|
||||
if (r.type == 'continue') continue
|
||||
if (r.type == 'break') break
|
||||
result = r
|
||||
break
|
||||
}
|
||||
|
||||
infinite.check()
|
||||
}
|
||||
leaveBlock()
|
||||
|
||||
return result
|
||||
|
||||
case 'WhileStatement':
|
||||
var infinite = InfiniteChecker(maxIterations)
|
||||
while (walk(node.test)){
|
||||
walk(node.body)
|
||||
infinite.check()
|
||||
}
|
||||
break
|
||||
|
||||
case 'TryStatement':
|
||||
try {
|
||||
walk(node.block)
|
||||
} catch (error) {
|
||||
enterBlock()
|
||||
var catchClause = node.handlers[0]
|
||||
if (catchClause) {
|
||||
blockContext[catchClause.param.name] = error
|
||||
walk(catchClause.body)
|
||||
}
|
||||
leaveBlock()
|
||||
} finally {
|
||||
if (node.finalizer) {
|
||||
walk(node.finalizer)
|
||||
}
|
||||
}
|
||||
break
|
||||
|
||||
case 'Literal':
|
||||
return node.value
|
||||
|
||||
case 'UnaryExpression':
|
||||
var val = walk(node.argument)
|
||||
switch(node.operator) {
|
||||
case '+': return +val
|
||||
case '-': return -val
|
||||
case '~': return ~val
|
||||
case '!': return !val
|
||||
case 'void': return void val
|
||||
case 'typeof': return typeof val
|
||||
default: return unsupportedExpression(node)
|
||||
}
|
||||
|
||||
case 'ArrayExpression':
|
||||
var obj = blockContext['Array']()
|
||||
for (var i=0;i<node.elements.length;i++){
|
||||
obj.push(walk(node.elements[i]))
|
||||
}
|
||||
return obj
|
||||
|
||||
case 'ObjectExpression':
|
||||
var obj = blockContext['Object']()
|
||||
for (var i = 0; i < node.properties.length; i++) {
|
||||
var prop = node.properties[i]
|
||||
var value = (prop.value === null) ? prop.value : walk(prop.value)
|
||||
obj[prop.key.value || prop.key.name] = value
|
||||
}
|
||||
return obj
|
||||
|
||||
case 'NewExpression':
|
||||
var args = node.arguments.map(function(arg){
|
||||
return walk(arg)
|
||||
})
|
||||
var target = walk(node.callee)
|
||||
return primitives.applyNew(target, args)
|
||||
|
||||
|
||||
case 'BinaryExpression':
|
||||
var l = walk(node.left)
|
||||
var r = walk(node.right)
|
||||
|
||||
switch(node.operator) {
|
||||
case '==': return l === r
|
||||
case '===': return l === r
|
||||
case '!=': return l != r
|
||||
case '!==': return l !== r
|
||||
case '+': return l + r
|
||||
case '-': return l - r
|
||||
case '*': return l * r
|
||||
case '/': return l / r
|
||||
case '%': return l % r
|
||||
case '<': return l < r
|
||||
case '<=': return l <= r
|
||||
case '>': return l > r
|
||||
case '>=': return l >= r
|
||||
case '|': return l | r
|
||||
case '&': return l & r
|
||||
case '^': return l ^ r
|
||||
case 'in': return l in r
|
||||
case 'instanceof': return l instanceof r
|
||||
default: return unsupportedExpression(node)
|
||||
}
|
||||
|
||||
case 'LogicalExpression':
|
||||
switch(node.operator) {
|
||||
case '&&': return walk(node.left) && walk(node.right)
|
||||
case '||': return walk(node.left) || walk(node.right)
|
||||
default: return unsupportedExpression(node)
|
||||
}
|
||||
|
||||
case 'ThisExpression':
|
||||
return blockContext['this']
|
||||
|
||||
case 'Identifier':
|
||||
if (node.name === 'undefined'){
|
||||
return undefined
|
||||
} else if (hasProperty(blockContext, node.name, primitives)){
|
||||
return finalValue(blockContext[node.name])
|
||||
} else {
|
||||
throw new ReferenceError(node.name + ' is not defined')
|
||||
}
|
||||
|
||||
case 'CallExpression':
|
||||
|
||||
var args = node.arguments.map(function(arg){
|
||||
return walk(arg)
|
||||
})
|
||||
var object = null
|
||||
var target = walk(node.callee)
|
||||
|
||||
if (node.callee.type === 'MemberExpression'){
|
||||
object = walk(node.callee.object)
|
||||
}
|
||||
return target.apply(object, args)
|
||||
|
||||
case 'MemberExpression':
|
||||
var obj = walk(node.object)
|
||||
if (node.computed){
|
||||
var prop = walk(node.property)
|
||||
} else {
|
||||
var prop = node.property.name
|
||||
}
|
||||
obj = primitives.getPropertyObject(obj, prop)
|
||||
return checkValue(obj[prop]);
|
||||
|
||||
case 'ConditionalExpression':
|
||||
var val = walk(node.test)
|
||||
return val ? walk(node.consequent) : walk(node.alternate)
|
||||
|
||||
case 'EmptyStatement':
|
||||
return
|
||||
|
||||
default:
|
||||
return unsupportedExpression(node)
|
||||
}
|
||||
}
|
||||
|
||||
// safely retrieve a value
|
||||
function checkValue(value){
|
||||
if (value === Function){
|
||||
value = safeFunction
|
||||
}
|
||||
return finalValue(value)
|
||||
}
|
||||
|
||||
// block scope context control
|
||||
function enterBlock(){
|
||||
blockContext = Object.create(blockContext)
|
||||
}
|
||||
function leaveBlock(){
|
||||
blockContext = Object.getPrototypeOf(blockContext)
|
||||
}
|
||||
|
||||
// set a value in the specified context if allowed
|
||||
function setValue(object, left, right, operator){
|
||||
var name = null
|
||||
|
||||
if (left.type === 'Identifier'){
|
||||
name = left.name
|
||||
// handle parent context shadowing
|
||||
object = objectForKey(object, name, primitives)
|
||||
} else if (left.type === 'MemberExpression'){
|
||||
if (left.computed){
|
||||
name = walk(left.property)
|
||||
} else {
|
||||
name = left.property.name
|
||||
}
|
||||
object = walk(left.object)
|
||||
}
|
||||
|
||||
// stop built in properties from being able to be changed
|
||||
if (canSetProperty(object, name, primitives)){
|
||||
switch(operator) {
|
||||
case undefined: return object[name] = walk(right)
|
||||
case '=': return object[name] = walk(right)
|
||||
case '+=': return object[name] += walk(right)
|
||||
case '-=': return object[name] -= walk(right)
|
||||
case '++': return object[name]++
|
||||
case '--': return object[name]--
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// when an unsupported expression is encountered, throw an error
|
||||
function unsupportedExpression(node){
|
||||
console.error(node)
|
||||
var err = new Error('Unsupported expression: ' + node.type)
|
||||
err.node = node
|
||||
throw err
|
||||
}
|
||||
|
||||
// walk a provided object's prototypal hierarchy to retrieve an inherited object
|
||||
function objectForKey(object, key, primitives){
|
||||
var proto = primitives.getPrototypeOf(object)
|
||||
if (!proto || hasOwnProperty(object, key)){
|
||||
return object
|
||||
} else {
|
||||
return objectForKey(proto, key, primitives)
|
||||
}
|
||||
}
|
||||
|
||||
function hasProperty(object, key, primitives){
|
||||
var proto = primitives.getPrototypeOf(object)
|
||||
var hasOwn = hasOwnProperty(object, key)
|
||||
if (object[key] !== undefined){
|
||||
return true
|
||||
} else if (!proto || hasOwn){
|
||||
return hasOwn
|
||||
} else {
|
||||
return hasProperty(proto, key, primitives)
|
||||
}
|
||||
}
|
||||
|
||||
function hasOwnProperty(object, key){
|
||||
return Object.prototype.hasOwnProperty.call(object, key)
|
||||
}
|
||||
|
||||
function propertyIsEnumerable(object, key){
|
||||
return Object.prototype.propertyIsEnumerable.call(object, key)
|
||||
}
|
||||
|
||||
|
||||
// determine if we have write access to a property
|
||||
function canSetProperty(object, property, primitives){
|
||||
if (property === '__proto__' || primitives.isPrimitive(object)){
|
||||
return false
|
||||
} else if (object != null){
|
||||
|
||||
if (hasOwnProperty(object, property)){
|
||||
if (propertyIsEnumerable(object, property)){
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
return canSetProperty(primitives.getPrototypeOf(object), property, primitives)
|
||||
}
|
||||
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// generate a function with specified context
|
||||
function getFunction(body, params, parentContext){
|
||||
return function(){
|
||||
var context = Object.create(parentContext),
|
||||
g = getGlobal()
|
||||
|
||||
context['window'] = context['global'] = g
|
||||
|
||||
if (this == g) {
|
||||
context['this'] = null
|
||||
} else {
|
||||
context['this'] = this
|
||||
}
|
||||
// normalize arguments array
|
||||
var args = Array.prototype.slice.call(arguments)
|
||||
context['arguments'] = arguments
|
||||
args.forEach(function(arg,idx){
|
||||
var param = params[idx]
|
||||
if (param){
|
||||
context[param] = arg
|
||||
}
|
||||
})
|
||||
var result = evaluateAst(body, context)
|
||||
|
||||
if (result instanceof ReturnValue){
|
||||
return result.value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function finalValue(value){
|
||||
if (value instanceof ReturnValue){
|
||||
return value.value
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
// get the name of an identifier
|
||||
function getName(identifier){
|
||||
return identifier.name
|
||||
}
|
||||
|
||||
// a ReturnValue struct for differentiating between expression result and return statement
|
||||
function ReturnValue(type, value){
|
||||
this.type = type
|
||||
this.value = value
|
||||
}
|
||||
|
||||
export default safeEval
|
||||
18
node_modules/@n8n_io/riot-tmpl/src/notevil/lib/infinite-checker.js
generated
vendored
Normal file
18
node_modules/@n8n_io/riot-tmpl/src/notevil/lib/infinite-checker.js
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
|
||||
function InfiniteChecker (maxIterations) {
|
||||
if (this instanceof InfiniteChecker) {
|
||||
this.maxIterations = maxIterations
|
||||
this.count = 0
|
||||
} else {
|
||||
return new InfiniteChecker(maxIterations)
|
||||
}
|
||||
}
|
||||
|
||||
InfiniteChecker.prototype.check = function () {
|
||||
this.count += 1
|
||||
if (this.count > this.maxIterations) {
|
||||
throw new Error('Infinite loop detected - reached max iterations')
|
||||
}
|
||||
}
|
||||
|
||||
export default InfiniteChecker
|
||||
117
node_modules/@n8n_io/riot-tmpl/src/notevil/lib/primitives.js
generated
vendored
Normal file
117
node_modules/@n8n_io/riot-tmpl/src/notevil/lib/primitives.js
generated
vendored
Normal file
@@ -0,0 +1,117 @@
|
||||
import { getGlobal } from '../helpers'
|
||||
|
||||
var names = ['Object', 'String', 'Boolean', 'Number', 'RegExp', 'Date', 'Array']
|
||||
var immutable = { string: 'String', boolean: 'Boolean', number: 'Number' }
|
||||
|
||||
var primitives = names.map(getGlobal)
|
||||
var protos = primitives.map(getProto)
|
||||
|
||||
function Primitives (context) {
|
||||
if (this instanceof Primitives) {
|
||||
this.context = context
|
||||
for (var i = 0; i < names.length; i++) {
|
||||
if (!this.context[names[i]]) {
|
||||
this.context[names[i]] = wrap(primitives[i])
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return new Primitives(context)
|
||||
}
|
||||
}
|
||||
|
||||
Primitives.prototype.replace = function (value) {
|
||||
var primIndex = primitives.indexOf(value),
|
||||
protoIndex = protos.indexOf(value),
|
||||
name
|
||||
|
||||
if (~primIndex) {
|
||||
name = names[primIndex]
|
||||
return this.context[name]
|
||||
} else if (~protoIndex) {
|
||||
name = names[protoIndex]
|
||||
return this.context[name].prototype
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
Primitives.prototype.getPropertyObject = function (object, property) {
|
||||
if (immutable[typeof object]) {
|
||||
return this.getPrototypeOf(object)
|
||||
}
|
||||
return object
|
||||
}
|
||||
|
||||
Primitives.prototype.isPrimitive = function (value) {
|
||||
return !!~primitives.indexOf(value) || !!~protos.indexOf(value)
|
||||
}
|
||||
|
||||
Primitives.prototype.getPrototypeOf = function (value) {
|
||||
if (value == null) { // handle null and undefined
|
||||
return value
|
||||
}
|
||||
|
||||
var immutableType = immutable[typeof value],
|
||||
proto
|
||||
|
||||
if (immutableType) {
|
||||
proto = this.context[immutableType].prototype
|
||||
} else {
|
||||
proto = Object.getPrototypeOf(value)
|
||||
}
|
||||
|
||||
if (!proto || proto === Object.prototype) {
|
||||
return null
|
||||
}
|
||||
|
||||
var replacement = this.replace(proto)
|
||||
|
||||
if (replacement === value) {
|
||||
replacement = this.replace(Object.prototype)
|
||||
}
|
||||
return replacement
|
||||
|
||||
}
|
||||
|
||||
Primitives.prototype.applyNew = function (func, args) {
|
||||
if (func.wrapped) {
|
||||
var prim = Object.getPrototypeOf(func)
|
||||
var instance = new (Function.prototype.bind.apply(prim, arguments))
|
||||
|
||||
setProto(instance, func.prototype)
|
||||
return instance
|
||||
}
|
||||
|
||||
return new (Function.prototype.bind.apply(func, arguments))
|
||||
|
||||
}
|
||||
|
||||
function getProto (func) {
|
||||
return func.prototype
|
||||
}
|
||||
|
||||
function setProto (obj, proto) {
|
||||
obj.__proto__ = proto // eslint-disable-line
|
||||
}
|
||||
|
||||
function wrap (prim) {
|
||||
var proto = Object.create(prim.prototype)
|
||||
|
||||
var result = function () {
|
||||
if (this instanceof result) {
|
||||
prim.apply(this, arguments)
|
||||
} else {
|
||||
var instance = prim.apply(null, arguments)
|
||||
|
||||
setProto(instance, proto)
|
||||
return instance
|
||||
}
|
||||
}
|
||||
|
||||
setProto(result, prim)
|
||||
result.prototype = proto
|
||||
result.wrapped = true
|
||||
return result
|
||||
}
|
||||
|
||||
export default Primitives
|
||||
99
node_modules/@n8n_io/riot-tmpl/src/skip-regex.js
generated
vendored
Normal file
99
node_modules/@n8n_io/riot-tmpl/src/skip-regex.js
generated
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
Regex detection.
|
||||
From: https://github.com/riot/parser/blob/master/src/skip-regex.js
|
||||
*/
|
||||
|
||||
var skipRegex = (function () { //eslint-disable-line no-unused-vars
|
||||
|
||||
// safe characters to precced a regex (including `=>`, `**`, and `...`)
|
||||
var beforeReChars = '[{(,;:?=|&!^~>%*/'
|
||||
|
||||
// keyword that can preceed a regex (`in` is handled as special case)
|
||||
var beforeReWords = [
|
||||
'case',
|
||||
'default',
|
||||
'do',
|
||||
'else',
|
||||
'in',
|
||||
'instanceof',
|
||||
'prefix',
|
||||
'return',
|
||||
'typeof',
|
||||
'void',
|
||||
'yield'
|
||||
]
|
||||
|
||||
var wordsLastChar = beforeReWords.reduce(function (s, w) {
|
||||
return s + w.slice(-1)
|
||||
}, '')
|
||||
|
||||
// The string to test can't include line-endings
|
||||
var RE_REGEX = /^\/(?=[^*>/])[^[/\\]*(?:(?:\\.|\[(?:\\.|[^\]\\]*)*\])[^[\\/]*)*?\/[gimuy]*/
|
||||
var RE_VN_CHAR = /[$\w]/
|
||||
|
||||
// Searches the position of the previous non-blank character inside `code`,
|
||||
// starting with `pos - 1`.
|
||||
function prev (code, pos) {
|
||||
while (--pos >= 0 && /\s/.test(code[pos]));
|
||||
return pos
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the code in the `start` position can be a regex.
|
||||
*
|
||||
* @param {string} code - Buffer to test in
|
||||
* @param {number} start - Position the first slash inside `code`
|
||||
* @returns {number} position of the char following the regex.
|
||||
*/
|
||||
function _skipRegex (code, start) {
|
||||
|
||||
// `exec()` will extract from the slash to the end of line and the
|
||||
// chained `match()` will match the possible regex.
|
||||
var re = /.*/g
|
||||
var pos = re.lastIndex = start++
|
||||
var match = re.exec(code)[0].match(RE_REGEX)
|
||||
|
||||
if (match) {
|
||||
var next = pos + match[0].length // result is not from `re.exec`
|
||||
|
||||
pos = prev(code, pos)
|
||||
var c = code[pos]
|
||||
|
||||
// start of buffer or safe prefix?
|
||||
if (pos < 0 || ~beforeReChars.indexOf(c)) {
|
||||
return next
|
||||
}
|
||||
|
||||
// from here, `pos` is >= 0 and `c` is code[pos]
|
||||
// is-tanbul ignore next: This is for ES6
|
||||
if (c === '.') {
|
||||
// can be `...` or something silly like 5./2
|
||||
if (code[pos - 1] === '.') {
|
||||
start = next
|
||||
}
|
||||
|
||||
} else if (c === '+' || c === '-') {
|
||||
// tricky case
|
||||
if (code[--pos] !== c || // if have a single operator or
|
||||
(pos = prev(code, pos)) < 0 || // ...have `++` and no previous token or
|
||||
!RE_VN_CHAR.test(code[pos])) { // ...the token is not a JS var/number
|
||||
start = next // ...this is a regex
|
||||
}
|
||||
|
||||
} else if (~wordsLastChar.indexOf(c)) {
|
||||
// keyword?
|
||||
var end = pos + 1
|
||||
|
||||
while (--pos >= 0 && RE_VN_CHAR.test(code[pos]));
|
||||
if (~beforeReWords.indexOf(code.slice(pos + 1, end))) {
|
||||
start = next
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return start
|
||||
}
|
||||
|
||||
return _skipRegex
|
||||
|
||||
})()
|
||||
424
node_modules/@n8n_io/riot-tmpl/src/tmpl.js
generated
vendored
Normal file
424
node_modules/@n8n_io/riot-tmpl/src/tmpl.js
generated
vendored
Normal file
@@ -0,0 +1,424 @@
|
||||
/**
|
||||
* @module tmpl
|
||||
*
|
||||
* tmpl - Root function, returns the template value, render with data
|
||||
* tmpl.hasExpr - Test the existence of a expression inside a string
|
||||
* tmpl.loopKeys - Get the keys for an 'each' loop (used by `_each`)
|
||||
*/
|
||||
//#if 0 // only in the unprocessed source
|
||||
/* eslint no-unused-vars: [2, {args: "after-used", varsIgnorePattern: "tmpl"}] */
|
||||
/* global brackets, riot */
|
||||
//#endif
|
||||
//#define LIST_GETTERS 0
|
||||
|
||||
// IIFE for tmpl()
|
||||
//#if ES6
|
||||
export
|
||||
//#endif
|
||||
var tmpl = (function () {
|
||||
//
|
||||
// Closure data
|
||||
// --------------------------------------------------------------------------
|
||||
var _cache = {}
|
||||
|
||||
//
|
||||
// Runtime Functions
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* The exposed tmpl function returns the template value from the cache, render with data.
|
||||
*
|
||||
* @param {string} str - Expression or template with zero or more expressions
|
||||
* @param {Object} data - A Tag instance, for setting the context
|
||||
* @returns {*} Raw value of the expression or template to render
|
||||
* @private
|
||||
*/
|
||||
function _tmpl (str, data) {
|
||||
if (!str) return str // catch falsy values here
|
||||
|
||||
//#if DEBUG
|
||||
/*eslint no-console: 0 */
|
||||
if (data && data._debug_) {
|
||||
data._debug_ = 0
|
||||
if (!_cache[str]) {
|
||||
_cache[str] = _create(str, 1) // request debug output
|
||||
var rs = typeof riot === 'undefined'
|
||||
? '(riot undefined)' : JSON.stringify(riot.settings)
|
||||
|
||||
console.log('--- DEBUG' +
|
||||
'\n riot.settings: ' + rs + '\n data: ' + JSON.stringify(data))
|
||||
}
|
||||
}
|
||||
//#endif
|
||||
|
||||
// At this point, the expressions must have been parsed, it only remains to construct
|
||||
// the function (if it is not in the cache) and call it to replace expressions with
|
||||
// their values. data (`this`) is a Tag instance, _logErr is the error handler.
|
||||
|
||||
return (_cache[str] || (_cache[str] = _create(str))).call(
|
||||
data, _logErr.bind({
|
||||
data: data,
|
||||
tmpl: str
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for an expression within a string, using the current brackets.
|
||||
*
|
||||
* @param {string } str - String where to search
|
||||
* @returns {boolean} `true` if the string contains an expression.
|
||||
* @function
|
||||
*/
|
||||
_tmpl.hasExpr = brackets.hasExpr
|
||||
|
||||
/**
|
||||
* Parses the `each` expression to detect how to map the collection data to the
|
||||
* children tags. Used by riot browser/tag/each.js
|
||||
*
|
||||
* {key, i in items} -> { key, pos, val }
|
||||
*
|
||||
* @param {String} expr - string passed in the 'each' attribute
|
||||
* @returns {Object} The object needed to check how the items in the collection
|
||||
* should be mapped to the children tags.
|
||||
* @function
|
||||
*/
|
||||
_tmpl.loopKeys = brackets.loopKeys
|
||||
|
||||
/**
|
||||
* Clears the internal cache of compiled expressions.
|
||||
*
|
||||
* @function
|
||||
*/
|
||||
// istanbul ignore next
|
||||
_tmpl.clearCache = function () { _cache = {} }
|
||||
|
||||
/**
|
||||
* Holds a custom function to handle evaluation errors.
|
||||
*
|
||||
* This property allows to detect errors _in the evaluation_, by setting its value to a
|
||||
* function that receives the generated Error object, augmented with an object `riotData`
|
||||
* containing the properties `tagName` and `_riot_id` of the context at error time.
|
||||
*
|
||||
* Other (usually fatal) errors, such as "Parse Error" generated by the Function
|
||||
* constructor, are not intercepted.
|
||||
*
|
||||
* If this property is not set, or set to falsy, as in previous versions the error
|
||||
* is silently ignored.
|
||||
*
|
||||
* @type {function}
|
||||
* @static
|
||||
*/
|
||||
_tmpl.errorHandler = null
|
||||
_tmpl.getStr = _getStr;
|
||||
|
||||
/**
|
||||
* Output an error message through the `_tmpl.errorHandler` function and
|
||||
* the console object.
|
||||
* @param {Error} err - The Error instance generated by the exception
|
||||
* @param {object} ctx - The context
|
||||
* @private
|
||||
*/
|
||||
function _logErr (err, ctx) {
|
||||
// add some data to the Error object
|
||||
err.riotData = {
|
||||
tagName: ctx && ctx.__ && ctx.__.tagName,
|
||||
_riot_id: ctx && ctx._riot_id //eslint-disable-line camelcase
|
||||
}
|
||||
|
||||
// user error handler
|
||||
if (_tmpl.errorHandler) _tmpl.errorHandler(err)
|
||||
else if (
|
||||
typeof console !== 'undefined' &&
|
||||
typeof console.error === 'function'
|
||||
) {
|
||||
console.error(err.message)
|
||||
console.log('<%s> %s', err.riotData.tagName || 'Unknown tag', this.tmpl) // eslint-disable-line
|
||||
console.log(this.data) // eslint-disable-line
|
||||
}
|
||||
}
|
||||
|
||||
function _getStr(str) {
|
||||
var expr = _getTmpl(str)
|
||||
|
||||
if (expr.slice(0, 11) !== 'try{return ') expr = 'return ' + expr
|
||||
|
||||
expr = 'var ' + (typeof window !== 'object' ? 'global' : 'window') + ' = {}; ' + expr
|
||||
|
||||
return expr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a function instance to get a value from the received template string.
|
||||
*
|
||||
* It'll halt the app if the expression has errors (Parse Error or SyntaxError).
|
||||
*
|
||||
* @param {string} str - The template. Can include zero or more expressions
|
||||
* @returns {Function} An instance of Function with the compiled template.
|
||||
* @private
|
||||
*/
|
||||
function _create (str) {
|
||||
var expr = _getTmpl(str)
|
||||
|
||||
if (expr.slice(0, 11) !== 'try{return ') expr = 'return ' + expr
|
||||
|
||||
// Create local variable called window to prevent access to global window object
|
||||
expr = 'var ' + (typeof window !== 'object' ? 'global' : 'window') + ' = {}; ' + expr
|
||||
|
||||
//#if DEBUG
|
||||
if (arguments.length > 1) console.log('--- getter:\n `' + expr + '`\n---')
|
||||
//#elif LIST_GETTERS
|
||||
//console.log(' In: `%s`\nOUT: `%s`', str, expr)
|
||||
//#endif
|
||||
/*#if CSP
|
||||
return safeEval.func('E', expr + ';')
|
||||
//#else */
|
||||
// Now, we can create the function to return by calling the Function constructor.
|
||||
// The parameter `E` is the error handler for runtime only.
|
||||
return new Function('E', expr + ';') // eslint-disable-line no-new-func
|
||||
//#endif
|
||||
}
|
||||
|
||||
//
|
||||
// Compilation
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
// Regexes for `_getTmpl` and `_parseExpr`
|
||||
var RE_DQUOTE = /\u2057/g
|
||||
var RE_QBMARK = /\u2057(\d+)~/g // string or regex marker, $1: array index
|
||||
|
||||
/**
|
||||
* Parses an expression or template with zero or more expressions enclosed with
|
||||
* the current brackets.
|
||||
*
|
||||
* @param {string} str - Raw template string, without comments
|
||||
* @returns {string} Processed template, ready for evaluation.
|
||||
* @private
|
||||
*/
|
||||
function _getTmpl (str) {
|
||||
var parts = brackets.split(str.replace(RE_DQUOTE, '"'), 1)// get text/expr parts
|
||||
var qstr = parts.qblocks // hidden qblocks
|
||||
var expr
|
||||
|
||||
// We can have almost anything as expressions, except comments... hope
|
||||
if (parts.length > 2 || parts[0]) {
|
||||
var i, j, list = []
|
||||
|
||||
for (i = j = 0; i < parts.length; ++i) {
|
||||
|
||||
expr = parts[i]
|
||||
|
||||
if (expr && (expr = i & 1 // every odd element is an expression
|
||||
|
||||
? _parseExpr(expr, 1, qstr) // mode 1 convert falsy values (except false) to "",
|
||||
// except zero
|
||||
: '"' + expr // ttext: convert to js literal string
|
||||
.replace(/\\/g, '\\\\') // this is html, preserve backslashes
|
||||
.replace(/\r\n?|\n/g, '\\n') // normalize eols
|
||||
.replace(/"/g, '\\"') + // escape inner double quotes
|
||||
'"' // enclose in double quotes
|
||||
|
||||
)) list[j++] = expr
|
||||
|
||||
}
|
||||
|
||||
expr = j < 2 ? list[0] // optimize code for 0-1 parts
|
||||
: '[' + list.join(',') + '].join("")'
|
||||
|
||||
} else {
|
||||
|
||||
expr = _parseExpr(parts[1], 0, qstr) // single expressions as raw value
|
||||
}
|
||||
|
||||
// Restore quoted strings and regexes
|
||||
if (qstr.length) {
|
||||
expr = expr.replace(RE_QBMARK, function (_, pos) {
|
||||
return qstr[pos]
|
||||
.replace(/\r/g, '\\r')
|
||||
.replace(/\n/g, '\\n')
|
||||
})
|
||||
}
|
||||
return expr
|
||||
}
|
||||
|
||||
var RE_CSNAME = /^(?:(-?[_A-Za-z\xA0-\xFF][-\w\xA0-\xFF]*)|\u2057(\d+)~):/
|
||||
var
|
||||
RE_BREND = {
|
||||
'(': /[()]/g,
|
||||
'[': /[[\]]/g,
|
||||
'{': /[{}]/g
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an individual expression `{expression}` or shorthand `{name: expression, ...}`
|
||||
*
|
||||
* For shorthand names, riot supports a limited subset of the full w3c/html specs of
|
||||
* non-quoted identifiers (closer to CSS1 that CSS2).
|
||||
*
|
||||
* The regex used for recognition is `-?[_A-Za-z\xA0-\xFF][-\w\xA0-\xFF]*`.
|
||||
*
|
||||
* This regex accepts almost all ISO-8859-1 alphanumeric characters within an html
|
||||
* identifier. Doesn't works with escaped codepoints, but you can use Unicode code points
|
||||
* beyond `\u00FF` by quoting the names (not recommended).
|
||||
*
|
||||
* @param {string} expr - The expression, without brackets
|
||||
* @param {number} asText - 0: raw value, 1: falsy (except false) as "", except 0
|
||||
* @param {Array} qstr - Where to store hidden quoted strings and regexes
|
||||
* @returns {string} Code to evaluate the expression.
|
||||
* @see {@link http://www.w3.org/TR/CSS21/grammar.html#scanner}
|
||||
* {@link http://www.w3.org/TR/CSS21/syndata.html#tokenization}
|
||||
* @private
|
||||
*/
|
||||
function _parseExpr (expr, asText, qstr) {
|
||||
|
||||
// Non-empty quoted strings and literal regexes are hidden at this point.
|
||||
//
|
||||
// Now, this function converts whitespace into compacted spaces and trims
|
||||
// surrounding spaces and some inner tokens, mainly brackets and separators.
|
||||
// We need to convert embedded `\r` and `\n` as these characters breaks
|
||||
// the evaluation.
|
||||
//
|
||||
// WARNING:
|
||||
// Trim and compact is not strictly necessary, but it allows optimized regexes.
|
||||
// Do not touch the next block until you know how/which regexes are affected.
|
||||
|
||||
expr = expr
|
||||
.replace(/\s+/g, ' ').trim()
|
||||
.replace(/\ ?([[\({},?\.:])\ ?/g, '$1')
|
||||
|
||||
if (expr) {
|
||||
var
|
||||
list = [],
|
||||
cnt = 0,
|
||||
match
|
||||
|
||||
// Try to match the first name in the possible shorthand list
|
||||
while (expr &&
|
||||
(match = expr.match(RE_CSNAME)) &&
|
||||
!match.index // index > 0 means error
|
||||
) {
|
||||
var
|
||||
key,
|
||||
jsb,
|
||||
re = /,|([[{(])|$/g
|
||||
|
||||
// Search the next unbracketed comma or the end of 'expr'.
|
||||
// If a openning js bracket is found ($1), skip the block,
|
||||
// if found the end of expr $1 will be empty and the while loop exits.
|
||||
|
||||
expr = RegExp.rightContext // before replace
|
||||
key = match[2] ? qstr[match[2]].slice(1, -1).trim().replace(/\s+/g, ' ') : match[1]
|
||||
|
||||
while (jsb = (match = re.exec(expr))[1]) skipBraces(jsb, re)
|
||||
|
||||
jsb = expr.slice(0, match.index)
|
||||
expr = RegExp.rightContext
|
||||
|
||||
list[cnt++] = _wrapExpr(jsb, 1, key)
|
||||
}
|
||||
|
||||
// For shorthands, the generated code returns an array with expression-name pairs
|
||||
expr = !cnt ? _wrapExpr(expr, asText)
|
||||
: cnt > 1 ? '[' + list.join(',') + '].join(" ").trim()' : list[0]
|
||||
}
|
||||
|
||||
return expr
|
||||
|
||||
// Skip bracketed block, uses the str value in the closure
|
||||
function skipBraces (ch, re) {
|
||||
var
|
||||
mm,
|
||||
lv = 1,
|
||||
ir = RE_BREND[ch]
|
||||
|
||||
ir.lastIndex = re.lastIndex
|
||||
while (mm = ir.exec(expr)) {
|
||||
if (mm[0] === ch) ++lv
|
||||
else if (!--lv) break
|
||||
}
|
||||
re.lastIndex = lv ? expr.length : ir.lastIndex
|
||||
}
|
||||
}
|
||||
|
||||
// Matches a varname, excludes object keys. $1: lookahead, $2: variable name
|
||||
// istanbul ignore next: not both
|
||||
var // eslint-disable-next-line max-len
|
||||
JS_CONTEXT = '"in this?this:' + (typeof window !== 'object' ? 'global' : 'window') + ').',
|
||||
JS_VARNAME = /[,{][\$\w]+(?=:)|(^ *|[^$\w\.{])(?!(?:typeof|true|false|null|undefined|in|instanceof|is(?:Finite|NaN)|void|NaN|new|Date|RegExp|Math)(?![$\w]))([$_A-Za-z][$\w]*)/g,
|
||||
JS_NOPROPS = /^(?=(\.[$\w]+))\1(?:[^.[(]|$)/
|
||||
|
||||
/**
|
||||
* Generates code to evaluate an expression avoiding breaking on undefined vars.
|
||||
*
|
||||
* This function include a try..catch block only if needed, if this block is not included,
|
||||
* the generated code has no return statement.
|
||||
*
|
||||
* This `isFinite`, `isNaN`, `Date`, `RegExp`, and `Math` keywords are not wrapped
|
||||
* for context detection (defaults to the global object).
|
||||
*
|
||||
* @param {string} expr - Normalized expression, without brackets
|
||||
* @param {boolean} asText - If trueish, the output is converted to text, not raw values
|
||||
* @param {string} [key] - For shorthands, the key name
|
||||
* @returns {string} Compiled expression.
|
||||
* @private
|
||||
*/
|
||||
function _wrapExpr (expr, asText, key) {
|
||||
var tb
|
||||
|
||||
expr = expr.replace(JS_VARNAME, function (match, p, mvar, pos, s) {
|
||||
if (mvar) {
|
||||
pos = tb ? 0 : pos + match.length // check only if needed
|
||||
|
||||
// this, window, and global needs try block too
|
||||
if (mvar !== 'this' && mvar !== 'global' && mvar !== 'window') {
|
||||
match = p + '("' + mvar + JS_CONTEXT + mvar
|
||||
if (pos) tb = (s = s[pos]) === '.' || s === '(' || s === '['
|
||||
} else if (pos) {
|
||||
tb = !JS_NOPROPS.test(s.slice(pos)) // needs try..catch block?
|
||||
}
|
||||
}
|
||||
return match
|
||||
})
|
||||
|
||||
if (tb) {
|
||||
expr = 'try{return ' + expr + '}catch(e){E(e,this)}'
|
||||
}
|
||||
|
||||
if (key) { // shorthands
|
||||
// w/try : function(){try{return expr}catch(e){E(e,this)}}.call(this)?"name":""
|
||||
// no try: (expr)?"name":""
|
||||
// ==> 'return [' + expr_list.join(',') + '].join(" ").trim()'
|
||||
expr = (tb
|
||||
? 'function(){' + expr + '}.call(this)' : '(' + expr + ')'
|
||||
) + '?"' + key + '":""'
|
||||
|
||||
} else if (asText) {
|
||||
// in multipart expression, falsy values resolve to empty string,
|
||||
// but `false` resolves to `false` instead
|
||||
if (expr === 'false') {
|
||||
expr = 'function(v){' + (tb
|
||||
? expr.replace('return ', 'v=') : 'v=(' + expr + ')'
|
||||
) + ';return false}.call(this)'
|
||||
} else {
|
||||
// w/try : function(v){try{v=expr}catch(e){E(e,this)};return v||v===0?v:""}.call(this)
|
||||
// no try: function(v){return (v=(expr))||v===0?v:""}.call(this)
|
||||
// ==> 'return [' + text_and_expr_list.join(',') + '].join("")'
|
||||
expr = 'function(v){' + (tb
|
||||
? expr.replace('return ', 'v=') : 'v=(' + expr + ')'
|
||||
) + ';return v||v===0||v===false?v:""}.call(this)'
|
||||
}
|
||||
}
|
||||
// else if (!asText)
|
||||
// no try: return expr
|
||||
// w/try : try{return expr}catch(e){E(e,this)} // returns undefined if error
|
||||
|
||||
return expr
|
||||
}
|
||||
|
||||
//#if !NODE
|
||||
_tmpl.version = brackets.version = 'WIP'
|
||||
//#endif
|
||||
|
||||
return _tmpl
|
||||
|
||||
})()
|
||||
Reference in New Issue
Block a user