File: js/vue/src/parsers/expression.js

Recommend this page to a friend!
  Classes of Sergey Beskorovayniy   Vue Resume   js/vue/src/parsers/expression.js   Download  
File: js/vue/src/parsers/expression.js
Role: Example script
Content type: text/plain
Description: Example script
Class: Vue Resume
Build reactive Web user interfaces
Author: By
Last change:
Date: 8 years ago
Size: 5,841 bytes
 

Contents

Class file image Download
import { warn } from '../util/index' import { parsePath, setPath } from './path' import Cache from '../cache' const expressionCache = new Cache(1000) const allowedKeywords = 'Math,Date,this,true,false,null,undefined,Infinity,NaN,' + 'isNaN,isFinite,decodeURI,decodeURIComponent,encodeURI,' + 'encodeURIComponent,parseInt,parseFloat' const allowedKeywordsRE = new RegExp('^(' + allowedKeywords.replace(/,/g, '\\b|') + '\\b)') // keywords that don't make sense inside expressions const improperKeywords = 'break,case,class,catch,const,continue,debugger,default,' + 'delete,do,else,export,extends,finally,for,function,if,' + 'import,in,instanceof,let,return,super,switch,throw,try,' + 'var,while,with,yield,enum,await,implements,package,' + 'protected,static,interface,private,public' const improperKeywordsRE = new RegExp('^(' + improperKeywords.replace(/,/g, '\\b|') + '\\b)') const wsRE = /\s/g const newlineRE = /\n/g const saveRE = /[\{,]\s*[\w\$_]+\s*:|('(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"|`(?:[^`\\]|\\.)*\$\{|\}(?:[^`\\]|\\.)*`|`(?:[^`\\]|\\.)*`)|new |typeof |void /g const restoreRE = /"(\d+)"/g const pathTestRE = /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['.*?'\]|\[".*?"\]|\[\d+\]|\[[A-Za-z_$][\w$]*\])*$/ const identRE = /[^\w$\.](?:[A-Za-z_$][\w$]*)/g const literalValueRE = /^(?:true|false|null|undefined|Infinity|NaN)$/ function noop () {} /** * Save / Rewrite / Restore * * When rewriting paths found in an expression, it is * possible for the same letter sequences to be found in * strings and Object literal property keys. Therefore we * remove and store these parts in a temporary array, and * restore them after the path rewrite. */ var saved = [] /** * Save replacer * * The save regex can match two possible cases: * 1. An opening object literal * 2. A string * If matched as a plain string, we need to escape its * newlines, since the string needs to be preserved when * generating the function body. * * @param {String} str * @param {String} isString - str if matched as a string * @return {String} - placeholder with index */ function save (str, isString) { var i = saved.length saved[i] = isString ? str.replace(newlineRE, '\\n') : str return '"' + i + '"' } /** * Path rewrite replacer * * @param {String} raw * @return {String} */ function rewrite (raw) { var c = raw.charAt(0) var path = raw.slice(1) if (allowedKeywordsRE.test(path)) { return raw } else { path = path.indexOf('"') > -1 ? path.replace(restoreRE, restore) : path return c + 'scope.' + path } } /** * Restore replacer * * @param {String} str * @param {String} i - matched save index * @return {String} */ function restore (str, i) { return saved[i] } /** * Rewrite an expression, prefixing all path accessors with * `scope.` and generate getter/setter functions. * * @param {String} exp * @return {Function} */ function compileGetter (exp) { if (improperKeywordsRE.test(exp)) { process.env.NODE_ENV !== 'production' && warn( 'Avoid using reserved keywords in expression: ' + exp ) } // reset state saved.length = 0 // save strings and object literal keys var body = exp .replace(saveRE, save) .replace(wsRE, '') // rewrite all paths // pad 1 space here because the regex matches 1 extra char body = (' ' + body) .replace(identRE, rewrite) .replace(restoreRE, restore) return makeGetterFn(body) } /** * Build a getter function. Requires eval. * * We isolate the try/catch so it doesn't affect the * optimization of the parse function when it is not called. * * @param {String} body * @return {Function|undefined} */ function makeGetterFn (body) { try { /* eslint-disable no-new-func */ return new Function('scope', 'return ' + body + ';') /* eslint-enable no-new-func */ } catch (e) { if (process.env.NODE_ENV !== 'production') { /* istanbul ignore if */ if (e.toString().match(/unsafe-eval|CSP/)) { warn( 'It seems you are using the default build of Vue.js in an environment ' + 'with Content Security Policy that prohibits unsafe-eval. ' + 'Use the CSP-compliant build instead: ' + 'http://vuejs.org/guide/installation.html#CSP-compliant-build' ) } else { warn( 'Invalid expression. ' + 'Generated function body: ' + body ) } } return noop } } /** * Compile a setter function for the expression. * * @param {String} exp * @return {Function|undefined} */ function compileSetter (exp) { var path = parsePath(exp) if (path) { return function (scope, val) { setPath(scope, path, val) } } else { process.env.NODE_ENV !== 'production' && warn( 'Invalid setter expression: ' + exp ) } } /** * Parse an expression into re-written getter/setters. * * @param {String} exp * @param {Boolean} needSet * @return {Function} */ export function parseExpression (exp, needSet) { exp = exp.trim() // try cache var hit = expressionCache.get(exp) if (hit) { if (needSet && !hit.set) { hit.set = compileSetter(hit.exp) } return hit } var res = { exp: exp } res.get = isSimplePath(exp) && exp.indexOf('[') < 0 // optimized super simple getter ? makeGetterFn('scope.' + exp) // dynamic getter : compileGetter(exp) if (needSet) { res.set = compileSetter(exp) } expressionCache.put(exp, res) return res } /** * Check if an expression is a simple path. * * @param {String} exp * @return {Boolean} */ export function isSimplePath (exp) { return pathTestRE.test(exp) && // don't treat literal values as paths !literalValueRE.test(exp) && // Math constants e.g. Math.PI, Math.E etc. exp.slice(0, 5) !== 'Math.' }