I have the following string named abc (which consists of javascript variables declarations):
var abc = `
var exp = 'test';
var test = 15;
var test2 = "wow";
`;
I would like to get the value of exp, test, test2 from this string.
One of the methods that might work is:
eval(abc);
However this solution is not suitable in typescript for security purposes, what other methods would you recommend ( feel free to propose some npm libraries) ?
Creating a function and passing the variable.
new Function has a closed scope in comparison to function eval.
var abc = `
var exp = 'test';
var test = 15;
var test2 = "wow";
`;
var fn = (variable) => (new Function('', `${abc} return ${variable};`))();
console.log(fn('exp'));
console.log(fn('test'));
console.log(fn('test2'));
One way to do that would be the following:
First we find the declarations by splitting the string using ; as delimiter
Now after we have found the declarations we use map() to extract the values of each one and return it into a new array.To find the value of a declaration we split it into 2 parts using '='.The first part is the declaration name and the second part is the declaration value
var abc = `var exp = 'test';
var test = 15;
var test2 = "wow";`;
//Trim string
abc = abc.trim();
//Split by ';'
var declarations = abc.split(';');
//The last item is empty so we remove it
declarations.pop();
console.log(declarations)
//Now that we have the declarations we need to get the values
//so we split each declaration using '='
var values = declarations.map(function(dec){
var value = dec.split("=").pop()
return value;
});
//Bonus
//This gets all declarations from a string and returns them as a key value object
function getDeclarations(s) {
var variables = s.split(";");
//Last variable is an empty string so pop it
variables.pop()
var declarations = {};
variables.forEach(function (variable) {
variable = variable.trim()
var name = variable.split("=")[0].split("var")[1];
var value = variable.split("=").pop();
name = name.trim();
value = value.trim();
declarations[name] = value;
});
return declarations;
}
console.log(values)
console.log(getDeclarations(abc))
Related
var variables = ['A','B'];
var allowedMathFunc = ['Sin','Cos','Tan']
var expression= 'Sin(B)+Tan(B)+100+acos(A)+C'
I want to validate expression string match below scenario
expression should use variables array values
expression should not contain other then allowed math functions.
I tried below code
var variables = ['A','B'];
var allowedMathFunc = ['Sin','Cos','Tan']
var expression= 'Sin(B)+Tan(B)+100+acos(A)'
for variable check I tried this
let expressionVariables = value.replace(/Sin|Log|Exp|Tan|Pos|Rnd|[^A-Za-z]/ig,"");
let expressionUnUsedVar= variables.filter(v=> !expressionVariables.includes(v));
I don't know how to write for both scenario for regex it's not a problem for two different regex.
You can use regex to extract all variables and function names, and then check them if they're included in the allowed function and variable lists (case sensitive):
function testExpression(expression, vars, funcs) {
const usedVariables = expression.match(/\b[a-z]+\b(?!\s*\()/gi);
const usedFunctions = expression.match(/\b[a-z]+\b(?=\s*\()/gi);
return usedVariables?.every(v => vars.includes(v)) && usedFunctions?.every(func => funcs.includes(func));
}
var variables = ['A','B'];
var allowedMathFunc = ['Sin','Cos','Tan']
var expression= 'Sin(B)+Tan(B)+100+acos(A)+C'
console.log(testExpression(expression, variables, allowedMathFunc));
console.log(testExpression('Sin(B)+Tan(B)+100+Cos(A)+C', ['A','B','C'], ['Sin','Cos','Tan']));
say I have the following string:
let code = `
var x = 4;
var y=9,
w=8
var z=8080
other()
x=12
`
How do I write a regex that gets all of the variable declarations? In this case, I want to return all statements, and no expressions, so exclude the other() and x=12 part. So far I have
let results = `
var x = 4;
var y=9,
w=8
var z=8080
other()
x=12
`.match(/(var)(.*?)(;|,|\n)/g);
console.log(results);
But I couldn't figure out how to include the w=8 part also, since that's also a declaration statement, while excluding the expressions. I tried .match(/(var|\n)(.*?)(;|,|\n)/g) but that also returns x=12, which I don't want. I need to also return all statement blocks even if there is a comma right before it (either on a previous line, or previous character, anything that would normally allow a variable declaration).
Any idea how to do this with regex?
EDIT:
with uglifyjs I'm able to do the following (which is the result I want):
let strin = `
var x = 4;
var y=9,
w=8,
kk = {
ok:1234
},
p,
a = undefined
var z=8080
other()
x=12
`
let results = UglifyJS.parse(strin).body.filter(x=>x.__proto__.TYPE == "Var")
.map(x=>
x.definitions.map(y=>({
name:y.name.name,
value:y.value ? findInPos(y.value.start, y.value.end,strin) : undefined
})),
).flat()
function findInPos(start, end,str) {
return str.substring(
start.pos,
end.endpos
);
}
<script type="text/javascript" src="https://yaakovyitzchak.github.io/coby/uglify.js">
</script>
But it seems a bit overkill just to extract the string value of each variable? (I don't need to evaluate it, just get the string value like so)
The task can be nicely solved with TypeScript Compiler API, as it can parse not only TypeScript but JavaScript code too. Since TypeScript compiles into JavaScript - there should be no problem to integrate the following TS snippet into a JS project.
See demo
import ts from "typescript";
const code = `
var x = 4;
var y=9,
w=8
var z=8080
other()
x=12
`;
const sf = ts.createSourceFile(
"test.js",
code,
ts.ScriptTarget.ES2017,
true,
ts.ScriptKind.JS,
);
sf.forEachChild((n) => {
if (ts.isVariableStatement(n)) {
n.declarationList.declarations.forEach((decl) => {
const name = decl.name.getText();
const value = decl.initializer?.getText();
console.log(`${name} = ${value}`);
});
}
});
The code above prints the following output to console:
x = 4
y = 9
w = 8
z = 8080
Hi I want to write clean code that I can read and have a good overview.
So I wrote this:
var id = '12345';
var coll = ['scc-roles','scc-proj-' + id];
var spm = 'some-role';
var data = {role : spm, roleNames : 'sccss-user', collection : coll}
var spmRoleId = xdmp.eval('declareUpdate();
var sec = require("/MarkLogic/security.xqy");
var roleId = sec.createRole(role, "Generated project member", roleNames, null, collection,null,null);
var uri = "http://marklogic.com/xdmp/roles/" + roleId;
xdmp.documentAddCollections(uri,collection)',data,{"database" : xdmp.securityDatabase()})
But apparently a newline is not allowed in xdmp.eval() ?
[javascript] JS-JAVASCRIPT: + 'var sec = require("/MarkLogic/security.xqy"); -- Error running JavaScript request: SyntaxError: Unexpected token ILLEGAL
I tried using a '+' sign to generate a strng over more then one line, swapping single and double quotes but no luck.
Being able to test this code (copy paste) to the security database makes a lot of sense to me...
If I wrap it all in one unreadable line , it works ok.
hugo
The way to effectively create a new line in a JavaScrit string is to escape the new line char like this
var str = "I'm displayed\
in two line";
In the final file, you will see effectively a new line.
If you want see in the dist output the new line but not in your src string you could just insert the \n equivalent of a return to line.
var str = "I'm displayed\n in two line";
In es6 you will be able to use ` char to achieve the same thing without \
var str = `I'm displayed
in two line`;
Maybe you would like the strange, yet useful array-notation way of doing this:
var multiline1 = [
'the lazy fox',
'jumped over',
'the dead chicken',
].join('\n');
and the result:
the lazy fox
jumped over
the dead chicken
In general, you should avoid string concatenation to build code for eval. Strings make it difficult to spot bugs and are a great vector for injection attacks. Instead, I'd advise you to write a proper function in XQuery or JavaScript and use xdmp.invokeFunction to evaluate it. invokeFunction takes all of the same options as xdmp.eval.
Here's an example that gets roles in the context of a security database. The applyAs function returns a function that wraps the function provided by the caller, evaluating it with the eval options provided.
function applyAs(fct, options) {
return function() {
var params = Array.prototype.slice.call(arguments);
// Curry the function to include the params by closure.
// xdmp.invokeFunction requires that invoked functions have
// an arity of zero.
var f = (function() {
return fct.apply(null, params);
}).bind(this);
// Allow passing in user name, rather than id
if(options.user) { options.userId = xdmp.user(options.user); delete options.user; }
// Allow the functions themselves to declare their transaction mode
if(fct.transactionMode && !(options.transactionMode)) { options.transactionMode = fct.transactionMode; }
return xdmp.invokeFunction(f, options); // xdmp.invokeFunction returns a ValueIterator
}
}
/**
* Gets an Array of id-name Objects. Requires privileged access to security.
*
* #param names An optional Array of role IDs as strings used to filter
* #return An Array of Objects with role ID keys and role name values
*/
function getRoles(names) {
var sec = require('/MarkLogic/security.xqy');
var db = {database: xdmp.securityDatabase()};
var roleIDs = applyAs(sec.getRoleIds, db);
var rolesItr;
if(Array.isArray(names)) {
rolesItr = roleIDs(xdmp.arrayValues(names));
} else {
rolesItr = roleIDs();
}
var roleNames = applyAs(sec.getRoleNames, db)(rolesItr).toArray().map(function(el) { return el.textContent; });
var roles = [];
var i = 0;
for(var role of rolesItr) {
var r = {}
r[role.textContent] = roleNames[i++];
roles.push(r);
}
return roles;
}
getRoles();
Originally from a gist.
I am new to javascript, how can I write this using for loop?
SortingPageAudio.prototype.cardAudios = function(cardNumber) {
var page = tabs.page;
var card1Audio = page.card1Audio;
var card2Audio = page.card2Audio;
var FRcard1Audio = card1Audio.replace("e_", "f_");
var FRcard2Audio = card2Audio.replace("e_", "f_");
if(cardNumber == '1')
{
playAudio.playFiles(FRcard1Audio));
}
else if(cardNumber == '2')
{
playAudio.playFiles(FRcard2Audio));
}
};
I don't see any need for a loop, as the function either does card 1 or card 2. If you want the code to be less repetitive, you can do this:
SortingPageAudio.prototype.cardAudios = function(cardNumber) {
var cardAudio = tabs.page["card" + cardNumber + "Audio"];
var FRcardAudio = cardAudio.replace("e_", "f_");
playAudio.playFiles(FRcardAudio);
};
In JavaScript, you can refer to a property either using dot notation and a property name literal (obj.foo), or using brackets notation and a property name string (obj["foo"]). In the latter case, the string can be the result of any expression.
If you wanted to call cardAudios for both cards, you could do it like this:
[1, 2].forEach(instance.cardAudios, instance);
(Where instance is a SortingPageAudio instance.)
Or with a simple for loop:
var card;
for (card = 1; card <= 2; ++card) {
instance.cardAudios(card);
}
Or of course:
instance.cardAudios(1);
instance.cardAudios(2);
I'm having a issue with something very simple. I am just wondering as to I can store these functions within an array. Check out some of the code below. I am unsure as to if this is correct as to how I am storing these functions. I am unsure as to if these functions should be within a object literal or array.This is not necessarily for a project, just good practice. Thanks!
//declaring a function
function alert_name(){
//declaring variables within a function asking user their name.
var username = prompt("Hey there, what is your name."," ");
//generating user input
var chameleon = "Welcome " + username;
//combinators
//alert("Welcome " + chameleon+ ", This is 'the website");
};
// inserting quotes into a string that is being alerted from the browser.
function otherTHings(){
var single = 'He said \'RUN\' ever so softly.';
//alert(single);
};
//running these functions and actually carry out the operations
//that have actually been declared into code above.
//string operations
function string_opertaions(){
var complete = "Com" + "plete";
//alert(complete);
// using combinators to do the same thing.
var sentance1 = "My name is";
var sentance2 = "someone";
var totalsenatces = sentance1 += sentance2;
//alert(totalsenatces);
};
//Booleans or true false values
function booleanys(){
var lying = false;
var truthful = true;
};
//Arrays very important. very similar to a object literal but different.
//Arrays store information or values or varibales/data.
var rack = [];
rack[0] = alert_name();
rack[1] = otherTHings();
rack[2] = string_opertaions();
rack[3] = booleanys();
//alert_name();
//otherTHings();
//string_opertaions();
//booleanys();
You are invoking the function and storing the result!
var rack = [];
rack[0] = alert_name;
rack[1] = otherTHings;
rack[2] = string_opertaions;
rack[3] = booleanys;