UNPKG

5.67 kBJavaScriptView Raw
1'use strict';
2
3var _ = require('lodash');
4var esprima = require('esprima');
5var rtError = require('./RTCodeError');
6var RTCodeError = rtError.RTCodeError;
7
8/**
9 * @param {string} code
10 * @param node
11 * @param {Context} context
12 */
13function validateJS(code, node, context) {
14 try {
15 esprima.parse(code);
16 } catch (e) {
17 throw RTCodeError.build(context, node, e.description);
18 }
19}
20
21/**
22 * @param {string} name
23 * @return {string}
24 */
25function normalizeName(name) {
26 return name.replace(/-/g, '_');
27}
28
29/**
30 * @param {string} txt
31 * @return {boolean}
32 */
33function isStringOnlyCode(txt) {
34 return (/^\s*\{.*}\s*$/g.test(txt)
35 );
36 //txt = txt.trim();
37 //return txt.length && txt.charAt(0) === '{' && txt.charAt(txt.length - 1) === '}';
38}
39
40/**
41 * @param {Array.<*>} array
42 * @param {*} obj
43 */
44function addIfMissing(array, obj) {
45 if (!_.includes(array, obj)) {
46 array.push(obj);
47 }
48}
49
50/**
51 * @param {Array.<string>} children
52 * @return {string}
53 */
54function concatChildren(children) {
55 var res = '';
56 _.forEach(children, function (child) {
57 if (child && !_.startsWith(child, ' /*')) {
58 res += ',';
59 }
60 res += child;
61 });
62 return res;
63}
64
65/**
66 * validate rt
67 * @param options
68 * @param {*} context
69 * @param {CONTEXT} reportContext
70 * @param node
71 */
72function validate(options, context, reportContext, node) {
73 if (node.type === 'tag' && node.attribs['rt-if'] && !node.attribs.key) {
74 var loc = rtError.getNodeLoc(context, node);
75 reportContext.warn('rt-if without a key', options.fileName, loc.pos.line, loc.pos.col, loc.start, loc.end);
76 }
77 if (node.type === 'tag' && node.attribs['rt-require'] && (node.attribs.dependency || node.attribs.as)) {
78 var _loc = rtError.getNodeLoc(context, node);
79 reportContext.warn("'rt-require' is obsolete, use 'rt-import' instead", options.fileName, _loc.pos.line, _loc.pos.col, _loc.start, _loc.end);
80 }
81 if (node.children) {
82 node.children.forEach(validate.bind(this, options, context, reportContext));
83 }
84}
85
86/**
87 * return true if any node in the given tree uses a scope name from the given set, false - otherwise.
88 * @param scopeNames a set of scope names to find
89 * @param node root of a syntax tree generated from an ExpressionStatement or one of its children.
90 */
91function usesScopeName(scopeNames, node) {
92 function usesScope(root) {
93 return usesScopeName(scopeNames, root);
94 }
95 if (_.isEmpty(scopeNames)) {
96 return false;
97 }
98 // rt-if="x"
99 if (node.type === 'Identifier') {
100 return _.includes(scopeNames, node.name);
101 }
102 // rt-if="e({key1: value1})"
103 if (node.type === 'Property') {
104 return usesScope(node.value);
105 }
106 // rt-if="e.x" or rt-if="e1[e2]"
107 if (node.type === 'MemberExpression') {
108 return node.computed ? usesScope(node.object) || usesScope(node.property) : usesScope(node.object);
109 }
110 // rt-if="!e"
111 if (node.type === 'UnaryExpression') {
112 return usesScope(node.argument);
113 }
114 // rt-if="e1 || e2" or rt-if="e1 | e2"
115 if (node.type === 'LogicalExpression' || node.type === 'BinaryExpression') {
116 return usesScope(node.left) || usesScope(node.right);
117 }
118 // rt-if="e1(e2, ... eN)"
119 if (node.type === 'CallExpression') {
120 return usesScope(node.callee) || _.some(node.arguments, usesScope);
121 }
122 // rt-if="f({e1: e2})"
123 if (node.type === 'ObjectExpression') {
124 return _.some(node.properties, usesScope);
125 }
126 // rt-if="e1[e2]"
127 if (node.type === 'ArrayExpression') {
128 return _.some(node.elements, usesScope);
129 }
130 return false;
131}
132
133/**
134 * @const
135 */
136var curlyMap = { '{': 1, '}': -1 };
137
138/**
139 * @typedef {{boundParams: Array.<string>, injectedFunctions: Array.<string>, html: string, options: *}} Context
140 */
141
142/**
143 * @typedef {{fileName:string,force:boolean,modules:string,defines:*,reactImportPath:string=,lodashImportPath:string=,flow:boolean,name:string,native:boolean,propTemplates:*,format:string,_:*,version:boolean,help:boolean,listTargetVersion:boolean,modules:string, dryRun:boolean}} Options
144 */
145
146/**
147 * @param node
148 * @param {Context} context
149 * @param {string} txt
150 * @return {string}
151 */
152function convertText(node, context, txt) {
153 var res = '';
154 var first = true;
155 var concatChar = node.type === 'text' ? ',' : '+';
156 while (_.includes(txt, '{')) {
157 var start = txt.indexOf('{');
158 var pre = txt.substr(0, start);
159 if (pre) {
160 res += (first ? '' : concatChar) + JSON.stringify(pre);
161 first = false;
162 }
163 var curlyCounter = 1;
164 var end = start;
165 while (++end < txt.length && curlyCounter > 0) {
166 curlyCounter += curlyMap[txt.charAt(end)] || 0;
167 }
168 if (curlyCounter === 0) {
169 var needsParens = start !== 0 || end !== txt.length - 1;
170 res += (first ? '' : concatChar) + (needsParens ? '(' : '') + txt.substr(start + 1, end - start - 2) + (needsParens ? ')' : '');
171 first = false;
172 txt = txt.substr(end);
173 } else {
174 throw RTCodeError.build(context, node, 'Failed to parse text \'' + txt + '\'');
175 }
176 }
177 if (txt) {
178 res += (first ? '' : concatChar) + JSON.stringify(txt);
179 }
180 if (res === '') {
181 res = 'true';
182 }
183 return res;
184}
185
186module.exports = {
187 usesScopeName: usesScopeName,
188 normalizeName: normalizeName,
189 validateJS: validateJS,
190 isStringOnlyCode: isStringOnlyCode,
191 concatChildren: concatChildren,
192 validate: validate,
193 addIfMissing: addIfMissing,
194 convertText: convertText
195};
\No newline at end of file