class Test
def initialize
end
def crash
print x
end
end
Test.new.crash
Clearly this snippet will crash at line 8. If you parse this with Opal, you will get this compiled code:
/* Generated by Opal 0.8.0.beta1 */
(function(Opal) {
Opal.dynamic_require_severity = "error";
var self = Opal.top, $scope = Opal, nil = Opal.nil, $breaker = Opal.breaker, $slice = Opal.slice, $klass = Opal.klass;
Opal.add_stubs(['$print', '$x', '$crash', '$new']);
(function($base, $super) {
function $Test(){};
var self = $Test = $klass($base, $super, 'Test', $Test);
var def = self.$$proto, $scope = self.$$scope;
def.$initialize = function() {
var self = this;
return nil;
};
return (def.$crash = function() {
var self = this;
return self.$print(self.$x());
}, nil) && 'crash';
})(self, null);
return $scope.get('Test').$new().$crash();
})(Opal);
And of course it will throw the same error.
However, is there a way to determine the Ruby line where this error comes from?
I can see this question: Is there a way to show the Ruby line numbers in javascript generated by Opal, but I don't understand the answer: it leads me to https://github.com/opal/opal/tree/0-6-stable/examples/rack and I'm not sure what am I supposed to be looking at or doing.
When I run my javascript, I have an index.html file that loads opal.min.js and opal-parser.min.js, then finally I have my compiled Ruby-Javascript code in a <script> tag.
Opal has source map support, to facilitate this kind of source level of debugging. I will not go into details about sourcemaps, but HTML5Rocks has a great article that covers the topic in depth.
Here is the minimal boilerplate to set that up with Opal:
Let index.rb be our source file:
class Test
def initialize
end
def crash
print x
end
end
Test.new.crash
Since you would rather not use a lot of extraneous utilties, let us directly use the Opal API. Create a file builder.rb which will compile the file above:
require 'opal'
Opal::Processor.source_map_enabled = true
Opal.append_path "."
builder = Opal::Builder.new.build('index')
# Write the output file containing a referece to sourcemap
# which we generate below : this will help the browser locate the
# sourcemap. Note that we are generating sourcemap for only code and not
# the entire Opal corelib.
#
File.binwrite "build.js", "#{builder.to_s}\n//# sourceMappingURL=build.js.map"
File.binwrite "build.js.map", builder.source_map.to_s
File.binwrite "opal_lib.js", Opal::Builder.build('opal_lib')
Also create an opal_lib.rb file containing only:
require 'opal'
Finally create an index.html which will allow us to run the script in browser.
<!DOCTYPE html>
<html>
<head>
<script src="opal_lib.js"></script>
<script src="build.js"></script>
</head>
<body>
</body>
</html>
Now to actually compile your file, run:
ruby builder.rb
This will generate compiled javascript files opal_lib.js and build.js which are referenced by our index.html file. Now just open index.html in your browser. You will get a full call-stack and source view:
The line numbers of the source file are available:
As an alternative to using the browser, you can also use Node.js for the same purpose. This requires you have Node.js and npm installed. You will also need to install npm module source-map-support
npm install source-map-support
Now you can open the node repl and enter the following:
require('source-map-support').install();
require('./opal_lib');
require('./build');
You will get a stack trace with correct source line numbers :
/home/gaurav/Workspace/opal-playground/opal_lib.js:4436
Error.captureStackTrace(err);
^
NoMethodError: undefined method `x' for #<Test:0x102>
at OpalClass.$new (/home/gaurav/Workspace/opal-playground/opal_lib.js:4436:15)
at OpalClass.$exception (/home/gaurav/Workspace/opal-playground/opal_lib.js:4454:31)
at $Test.$raise (/home/gaurav/Workspace/opal-playground/opal_lib.js:4204:31)
at $Test.Opal.defn.TMP_1 (/home/gaurav/Workspace/opal-playground/opal_lib.js:3032:19)
at $Test.method_missing_stub [as $x] (/home/gaurav/Workspace/opal-playground/opal_lib.js:886:35)
at $Test.$crash (/home/gaurav/Workspace/opal-playground/index.rb:8:11)
at /home/gaurav/Workspace/opal-playground/index.rb:13:10
at Object.<anonymous> (/home/gaurav/Workspace/opal-playground/index.rb:13:10)
at Module._compile (module.js:435:26)
at Object.Module._extensions..js (module.js:442:10)
I recommend that you use bundler for gem management. Here is the Gemfile for fetching Opal master:
source 'http://production.cf.rubygems.org'
gem 'opal', github: 'opal/opal'
To compile you will have to run:
bundle install
bundle exec ruby builder.rb
Sprockets integration / rack integration that others have mentioned use the same API underneath, abstracting away the plumbing.
Update:
Since we have the correct line numbers in stack, it is fairly to programatically parse the stack and extract this line number into a variable:
require('./opal_lib');
require('source-map-support').install();
var $e = null;
try {
require('./build');
} catch (e) {
$e = e;
}
var lines = e.split('\n').map(function(line){ return line.match(/^.*\((\S+):(\d+):(\d+)\)/) })
var first_source_line;
for (var i = 0; i < lines.length ; i++) {
var match = lines[i];
if (match == null) continue;
if (match[1].match(/index.rb$/) {
first_source_line = match;
break;
}
}
var line_number;
if (first_source_line) line_number = first_source_line[2] // ==> 8
And of course you can do it ruby as well (but if you are running this in browser you will have to include source-map-support here as well):
class Test
def initialize
end
def crash
print x
end
end
line_num = nil
begin
Test.new.crash
rescue => e
if line = e.backtrace.map{|line| line.match(/^.*\((\S+):(\d+):(\d+)\)/) }.compact.find{|match| match[1] =~ /index.rb$/ }
line_num = line[2]
end
end
puts "line_num => #{line_num}" # ==> 8
Related
Problem Statement
I wrote a JavaScript code to pull data from Uniswap V3 SDK (https://www.npmjs.com/package/#uniswap/v3-sdk) and I would like to open-source this code on GitHub because currently this isn't possible (for specific use cases). I am not an amazing system designer/programmer so please bare with me (I can only code in Python and barely understand JavaScript).
Essentially, I have a JavaScript code that pulls data and I wanted to wrap it in a Python code, so I can then take the data and do other things with the data (create alerts, flask app, etc.)
I am using Ubuntu, JS6, npm v8.19.2 and Node v16.18.1.
In my research, I found two packages that could help enable me wrapping this JS code in Python:
py2js - https://github.com/PiotrDabkowski/Js2Py
stpyv8 - https://github.com/cloudflare/stpyv8
Coding Attempt
py2js attempt
This package only works for JS5 and below, not JS6. Here is a quote from the GitHub that helps explain the workaround for this issue (which uses babel) https://github.com/PiotrDabkowski/Js2Py:
JavaScript 6 support was achieved by using Js2Py to translate javascript library called Babel. Babel translates JS 6 to JS 5 and afterwards Js2Py translates JS 5 to Python.
While trying this solution out, I am getting the following error:
/home/bobby/uni_balances/uni_venv/lib/python3.10/site-packages/js2py/es6/__init__.py:10: UserWarning:
Importing babel.py for the first time - this can take some time.
Please note that currently Javascript 6 in Js2Py is unstable and slow. Use only for tiny scripts!
warnings.warn(
/home/bobby/uni_balances/uni_venv/lib/python3.10/site-packages/js2py/base.py:2854: FutureWarning: Possible nested set at position 5
self.pat = re.compile(
Initialised babel!
Traceback (most recent call last):
File "/home/bobby/uni_balances/main.py", line 104, in <module>
result = js2py.eval_js6(js) # executing JavaScript and converting the result to python string
File "/home/bobby/uni_balances/uni_venv/lib/python3.10/site-packages/js2py/evaljs.py", line 120, in eval_js6
return eval_js(js6_to_js5(js))
File "/home/bobby/uni_balances/uni_venv/lib/python3.10/site-packages/js2py/evaljs.py", line 115, in eval_js
return e.eval(js)
File "/home/bobby/uni_balances/uni_venv/lib/python3.10/site-packages/js2py/evaljs.py", line 204, in eval
self.execute(code, use_compilation_plan=use_compilation_plan)
File "/home/bobby/uni_balances/uni_venv/lib/python3.10/site-packages/js2py/evaljs.py", line 199, in execute
exec (compiled, self._context)
File "<EvalJS snippet>", line 4, in <module>
File "<EvalJS snippet>", line 3, in PyJs_LONG_0_
File "/home/bobby/uni_balances/uni_venv/lib/python3.10/site-packages/js2py/base.py", line 949, in __call__
return self.call(self.GlobalObject, args)
File "/home/bobby/uni_balances/uni_venv/lib/python3.10/site-packages/js2py/base.py", line 1464, in call
return Js(self.code(*args))
File "/home/bobby/uni_balances/uni_venv/lib/python3.10/site-packages/js2py/host/jseval.py", line 17, in Eval
py_code = translate_js(code.to_string().value, '')
File "/home/bobby/uni_balances/uni_venv/lib/python3.10/site-packages/js2py/translators/translator.py", line 70, in translate_js
parsed = parse_fn(js)
File "/home/bobby/uni_balances/uni_venv/lib/python3.10/site-packages/js2py/translators/translator.py", line 62, in pyjsparser_parse_fn
return parser.parse(code)
File "/home/bobby/uni_balances/uni_venv/lib/python3.10/site-packages/pyjsparser/parser.py", line 3008, in parse
program = self.parseProgram()
File "/home/bobby/uni_balances/uni_venv/lib/python3.10/site-packages/pyjsparser/parser.py", line 2974, in parseProgram
body = self.parseScriptBody()
File "/home/bobby/uni_balances/uni_venv/lib/python3.10/site-packages/pyjsparser/parser.py", line 2963, in parseScriptBody
statement = self.parseStatementListItem()
File "/home/bobby/uni_balances/uni_venv/lib/python3.10/site-packages/pyjsparser/parser.py", line 2110, in parseStatementListItem
return self.parseStatement()
File "/home/bobby/uni_balances/uni_venv/lib/python3.10/site-packages/pyjsparser/parser.py", line 2718, in parseStatement
self.consumeSemicolon()
File "/home/bobby/uni_balances/uni_venv/lib/python3.10/site-packages/pyjsparser/parser.py", line 1120, in consumeSemicolon
self.throwUnexpectedToken(self.lookahead)
File "/home/bobby/uni_balances/uni_venv/lib/python3.10/site-packages/pyjsparser/parser.py", line 1046, in throwUnexpectedToken
raise self.unexpectedTokenError(token, message)
js2py.internals.simplex.JsException: SyntaxError: Line 34: Unexpected token function
What have I done to try to fix this?
I have updated node and even downgraded to version 5 as some suggested, but nothing worked.
JavaScript Code
Here is the JavaScript code (note that you would need an Alchemy API ID to run this script):
import { JSBI } from "#uniswap/sdk";
import { ethers } from 'ethers';
import * as fs from 'fs';
// ERC20 json abi file
let ERC20Abi = fs.readFileSync('Erc20.json');
const ERC20 = JSON.parse(ERC20Abi);
// V3 pool abi json file
let pool = fs.readFileSync('V3PairAbi.json');
const IUniswapV3PoolABI = JSON.parse(pool);
// V3 factory abi json
let facto = fs.readFileSync('V3factory.json');
const IUniswapV3FactoryABI = JSON.parse(facto);
let NFT = fs.readFileSync('UniV3NFT.json');
const IUniswapV3NFTmanagerABI = JSON.parse(NFT);
const provider = new ethers.providers.JsonRpcProvider(ALCHEMY_API_ID)
// V3 standard addresses (different for celo)
const factory = "0x1F98431c8aD98523631AE4a59f267346ea31F984";
const NFTmanager = "0xC36442b4a4522E871399CD717aBDD847Ab11FE88";
async function getData(tokenID){
let FactoryContract = new ethers.Contract(factory, IUniswapV3FactoryABI, provider);
let NFTContract = new ethers.Contract(NFTmanager, IUniswapV3NFTmanagerABI, provider);
let position = await NFTContract.positions(tokenID);
let token0contract = new ethers.Contract(position.token0, ERC20, provider);
let token1contract = new ethers.Contract(position.token1, ERC20, provider);
let token0Decimal = await token0contract.decimals();
let token1Decimal = await token1contract.decimals();
let token0sym = await token0contract.symbol();
let token1sym = await token1contract.symbol();
let V3pool = await FactoryContract.getPool(position.token0, position.token1, position.fee);
let poolContract = new ethers.Contract(V3pool, IUniswapV3PoolABI, provider);
let slot0 = await poolContract.slot0();
let pairName = token0sym +"/"+ token1sym;
let dict = {"SqrtX96" : slot0.sqrtPriceX96.toString(), "Pair": pairName, "T0d": token0Decimal, "T1d": token1Decimal, "tickLow": position.tickLower, "tickHigh": position.tickUpper, "liquidity": position.liquidity.toString()}
return dict
}
const Q96 = JSBI.exponentiate(JSBI.BigInt(2), JSBI.BigInt(96));
const MIN_TICK = -887272;
const MAX_TICK = 887272;
function getTickAtSqrtRatio(sqrtPriceX96){
let tick = Math.floor(Math.log((sqrtPriceX96/Q96)**2)/Math.log(1.0001));
return tick;
}
async function getTokenAmounts(liquidity,sqrtPriceX96,tickLow,tickHigh,token0Decimal,token1Decimal){
let sqrtRatioA = Math.sqrt(1.0001**tickLow).toFixed(18);
let sqrtRatioB = Math.sqrt(1.0001**tickHigh).toFixed(18);
let currentTick = getTickAtSqrtRatio(sqrtPriceX96);
let sqrtPrice = sqrtPriceX96 / Q96;
let amount0wei = 0;
let amount1wei = 0;
if(currentTick <= tickLow){
amount0wei = Math.floor(liquidity*((sqrtRatioB-sqrtRatioA)/(sqrtRatioA*sqrtRatioB)));
}
if(currentTick > tickHigh){
amount1wei = Math.floor(liquidity*(sqrtRatioB-sqrtRatioA));
}
if(currentTick >= tickLow && currentTick < tickHigh){
amount0wei = Math.floor(liquidity*((sqrtRatioB-sqrtPrice)/(sqrtPrice*sqrtRatioB)));
amount1wei = Math.floor(liquidity*(sqrtPrice-sqrtRatioA));
}
let amount0Human = (amount0wei/(10**token0Decimal)).toFixed(token0Decimal);
let amount1Human = (amount1wei/(10**token1Decimal)).toFixed(token1Decimal);
console.log("Amount Token0 wei: "+amount0wei);
console.log("Amount Token1 wei: "+amount1wei);
console.log("Amount Token0 : "+amount0Human);
console.log("Amount Token1 : "+amount1Human);
return [amount0wei, amount1wei]
}
async function start(positionID){
let data = await getData(positionID);
let tokens = await getTokenAmounts(data.liquidity, data.SqrtX96, data.tickLow, data.tickHigh, data.T0d, data.T1d);
}
start(273381)
// Also it can be used without the position data if you pull the data it will work for any range
getTokenAmounts(12558033400096537032, 20259533801624375790673555415)
Python Code
Here is the python code I wrote using the js2py package. To save space, I removed the JS Code in the js variable below, but you can copy it from above and place it in the triple quotes:
import js2py
js = """
<<< JS CODE COPIED HERE >>>
""".replace("document.write", "return ")
result = js2py.eval_js6(js) # executing JavaScript and converting the result to python string
One other note, you will need to install babel as well to make it work. Babel will help convert the code from JS6 to JS5.
stpyv8 attempt
This package is maintained by Cloudflare, which makes this a much more attractive approach, but I am having difficulty installing the pyv8 package on my machine.
I was able to successfully run:
sudo apt install python3 python3-dev build-essential libboost-dev libboost-system-dev libboost-python-dev libboost-iostreams-dev
The probem is that I now need to run setup.py, which I can't find. I also below, if you look at the pyv8 installation I tried below, it does it automatically, but it throws an error. Seems like I am getting stuck at trying to run setup.py
pyv8 attempt (alternative to stpyv8 I believe)
https://code.google.com/archive/p/pyv8/
When I run pip3 install -v pyv8, I get the following error:
Using pip 22.0.2 from /home/bobby/uni_balances/uni_venv/lib/python3.10/site-packages/pip (python 3.10)
Collecting pyv8
Using cached PyV8-0.5.zip (22 kB)
Running command python setup.py egg_info
running egg_info
creating /tmp/pip-pip-egg-info-g__o7shf/PyV8.egg-info
writing /tmp/pip-pip-egg-info-g__o7shf/PyV8.egg-info/PKG-INFO
writing dependency_links to /tmp/pip-pip-egg-info-g__o7shf/PyV8.egg-info/dependency_links.txt
writing top-level names to /tmp/pip-pip-egg-info-g__o7shf/PyV8.egg-info/top_level.txt
writing manifest file '/tmp/pip-pip-egg-info-g__o7shf/PyV8.egg-info/SOURCES.txt'
reading manifest file '/tmp/pip-pip-egg-info-g__o7shf/PyV8.egg-info/SOURCES.txt'
writing manifest file '/tmp/pip-pip-egg-info-g__o7shf/PyV8.egg-info/SOURCES.txt'
Preparing metadata (setup.py) ... done
Using legacy 'setup.py install' for pyv8, since package 'wheel' is not installed.
Installing collected packages: pyv8
Running command Running setup.py install for pyv8
running install
/home/bobby/uni_balances/uni_venv/lib/python3.10/site-packages/setuptools/command/install.py:34: SetuptoolsDeprecationWarning: setup.py install is deprecated. Use build and pip and other standards-based tools.
warnings.warn(
running build
running build_py
creating build
creating build/lib.linux-x86_64-3.10
copying PyV8.py -> build/lib.linux-x86_64-3.10
running build_ext
building '_PyV8' extension
creating build/temp.linux-x86_64-3.10
creating build/temp.linux-x86_64-3.10/src
x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC -DBOOST_PYTHON_STATIC_LIB -Ilib/python/inc -Ilib/boost/inc -Ilib/v8/inc -I/home/bobby/uni_balances/uni_venv/include -I/usr/include/python3.10 -c src/Context.cpp -o build/temp.linux-x86_64-3.10/src/Context.o
In file included from src/Wrapper.h:8,
from src/Context.h:7,
from src/Context.cpp:1:
src/Exception.h:6:10: fatal error: v8.h: No such file or directory
6 | #include <v8.h>
| ^~~~~~
compilation terminated.
error: command '/usr/bin/x86_64-linux-gnu-gcc' failed with exit code 1
error: subprocess-exited-with-error
× Running setup.py install for pyv8 did not run successfully.
│ exit code: 1
╰─> See above for output.
note: This error originates from a subprocess, and is likely not a problem with pip.
full command: /home/bobby/uni_balances/uni_venv/bin/python3 -u -c '
exec(compile('"'"''"'"''"'"'
# This is <pip-setuptools-caller> -- a caller that pip uses to run setup.py
#
# - It imports setuptools before invoking setup.py, to enable projects that directly
# import from `distutils.core` to work with newer packaging standards.
# - It provides a clear error message when setuptools is not installed.
# - It sets `sys.argv[0]` to the underlying `setup.py`, when invoking `setup.py` so
# setuptools doesn'"'"'t think the script is `-c`. This avoids the following warning:
# manifest_maker: standard file '"'"'-c'"'"' not found".
# - It generates a shim setup.py, for handling setup.cfg-only projects.
import os, sys, tokenize
try:
import setuptools
except ImportError as error:
print(
"ERROR: Can not execute `setup.py` since setuptools is not available in "
"the build environment.",
file=sys.stderr,
)
sys.exit(1)
__file__ = %r
sys.argv[0] = __file__
if os.path.exists(__file__):
filename = __file__
with tokenize.open(__file__) as f:
setup_py_code = f.read()
else:
filename = "<auto-generated setuptools caller>"
setup_py_code = "from setuptools import setup; setup()"
exec(compile(setup_py_code, filename, "exec"))
'"'"''"'"''"'"' % ('"'"'/tmp/pip-install-b9d0zoul/pyv8_c9dfd2e4ec0e4c66a2bff47f44972004/setup.py'"'"',), "<pip-setuptools-caller>", "exec"))' install --record /tmp/pip-record-pel12p8h/install-record.txt --single-version-externally-managed --compile --install-headers /home/bobby/uni_balances/uni_venv/include/site/python3.10/pyv8
cwd: /tmp/pip-install-b9d0zoul/pyv8_c9dfd2e4ec0e4c66a2bff47f44972004/
Running setup.py install for pyv8 ... error
error: legacy-install-failure
× Encountered error while trying to install package.
╰─> pyv8
note: This is an issue with the package mentioned above, not pip.
hint: See above for output from the failure.
Conclusion
I am not married to using the two options I mentioned above, but I would like to pull the data into Python from the JS code.
Also this is the output from the JS code that I need to port over into Python:
I need to check folders, files and file details (file size) for given directory using JavaScript in standalone karate.jar and this validation is part of test automation script.
I understand this can be achieved by writing custom java packages in maven and use them in JavaScript of karate script.
How to implement this in standalone karate.jar. Pass directory path and directory name as two arguments to JavaScript functions which validate given directory name exists in provided directory path. Its kind of handling file system objects.
I understand the karate supports only read the files and not the read folder names and file names/ file details in directory.
Read files in karate script : https://intuit.github.io/karate/#reading-files
The allowed file extensions are : .json, .xml, .yaml, .csv, .txt, .feature
Please guide me how to implement this using Javascript in karate?
Thanks
Chandra.
Yes this is harder with the standalone JAR. Here are some suggestions:
write a small JAR file (one-time) that you can include in your stand-alone project: https://stackoverflow.com/a/56458094/143475
use OS commands via karate.exec(), e.g. on windows * def homePath = karate.exec('cmd /c echo %HOMEPATH%')
with some work, you can create pure JS scripts that use the JVM libraries, but you need a good understanding of what exists in Java and these are very hard to debug so I don't recommend this approach. I am giving an example below:
* def findFile =
"""
function(file, condition) {
var root = new java.io.File(file);
function recurse(file) {
var list = file.listFiles();
for (var i = 0; i < list.length; i++) {
var f = list[i];
if (f.directory) {
// karate.log('recurse:', f);
return recurse(f);
} else {
var path = f.path;
// karate.log('scan:', path);
if (condition(path)) {
karate.log('*** found:', path);
return f;
}
}
}
}
return recurse(root);
}
"""
* def filter = function(x){ return x.contains('/test-') && x.endsWith('.log') }
* def found = findFile('.', filter)
* print 'found:', found
I have a really simple website (ASP.NET core) that is a single .html static page and 6 .js files.
In one of the js files are some data that is based on my configuration:
localhost
dev
production
right now, it's hardcoded for my localhost.
Is there way that I can build/package the simple app so that if i say dev or prod in some command line arg, it replaces those values with something from somewhere else?
eg.
in main.js:
var environment = "localhost";
var rooturl = "https://localhost:43210";
and lets imagine i wish to build to my dev server...
var environment = "dev";
var rooturl = "https://pewpew.azurewebsites.com";
Is this possible? To keep things simple, assume I know nothing of JS tools and processes. (it's actually the truth, but lets not tell anyone that).
Update (further clarifications):
with 1x static html file and 6x static JS files, I have a static website. So i'm hoping to generate the js files as static files (still) but with the environment data already compiled in it.
you can use some build tools like grunt. where you can define build task which takes the environment parameter and change the variables to the desired values.
another (more simple) way is to dynamicaly create main.js (with dependency on the environment) file with your backend and the frontend will load it when it starts. src of the script tag can be the asp script, where the output is javascript
This is a snippet from a project in which I do just that. I replace various place holders with values stored in the environment variables.
This example is based on a linux environment, so I used sed to modify the file in-place, however you could just as easily read the file into memory, do the replace and write it back to disk.
grunt.task.registerTask('secretkeys', 'Replace various keys', function() {
var oauth;
try{
oauth = JSON.parse(process.env.oauthKeys).oauth;
}
catch(e){
oauth = {google:{}};
}
var replaces = {
'==GOOGLECLIENTID==':oauth.google.client_id || '{**GOOGLECLIENTID**}',
'==GOOGLESECRETKEY==':oauth.google.client_secret || '{**GOOGLESECRETKEY**}',
'==SECRETKEY==':oauth.secret || '{**SECRETKEY**}',
'==LOCALAUTH==':oauth.login,
};
const child = require('child_process');
grunt.file.expand('bin/**/*.json').forEach(function(file) {
grunt.log.write(`${file} \n`);
for(var key in replaces){
var cmd = 'sed -i s~{{orig}}~{{new}}~g {{file}}'
.replace(/{{file}}/g,file)
.replace(/{{orig}}/g,key.replace(/~/g,'\\~'))
.replace(/{{new}}/g,replaces[key].replace(/~/g,'\\~'))
;
grunt.log.write(` - ${key} \n`);
//grunt.log.write(` ${cmd} \n`);
child.execSync(cmd);
}
});
});
Hopefully you can modify to your purposes.
EDIT : I am reconsidering my answer, you are modifying javascript on a windows environment. You are likely better using PowerShell
(gc script.js) `
.replace("==GOOGLECLIENTID==",$Env:GoogleClientId) `
.replace("==SECRETKEY==",$Env:SecretKey) `
> script-build.js
So after re-reading your question, I realize there is a better solution that I have used in the past. My other answer is still relevant, so I'll leave it.
It may be simplest to just create a config file in the same folder.
<html>
<head>
<script type="text/javascript" src="config.js" ></script>
<script type="text/javascript" src="myscript.js" ></script>
</head>
<body>
ask me your questions, bridgekeeper
</body>
</html>
config.js
var config = {
'colour': 'yellow'
};
myscript.js
var user = prompt("What is your favourite colour?", "");
if(user !== config.colour){
alert("No BLUE! Ahhh....");
}
else{
alert("You may pass");
}
This is the technique I use when developing simple HTA apps for use around the office.
Check out envify. You can run it from the command line. https://github.com/hughsk/envify
sudo npm install -g envify
Say you have
var myVar = process.env.MYVAR;
Run from the command line
MYVAR=somevalue envify input.js > output.js
and the output js file should have
var myVar = 'somevalue';
My goal was to create a file that would
Require all of the JS files in a directory that didn't end in _test.js
Do a module.exports equal to an array of module names returned from those view files.
I thought I had it with this:
// Automagically crawls through this directory, finds every js file inside any
// subdirectory, removes test files, and requires the resulting list of files,
// registering the exported module names as dependencies to the myApp.demoApp.views module.
var context = require.context('.', true, /\/.*\/.*\.js$/);
var moduleNames = _.chain(context.keys())
.filter(function(key) {
console.log(key, key.indexOf('_test.js') == -1);
return key.indexOf('_test.js') == -1;
})
.map(function(key) {
console.log("KEY", key);
return context(key)
})
.value();
module.exports = angular.module('myApp.demoApp.views', moduleNames).name;
#2 is working as intended
#1 Unfortunately I was naive. While the module names are filtered out, this still requires all of the files with _test so the test files end up in my built code.
I tried to fix this by updating the regex but JS doesn't support regex negative-look-behind and I'm not regex savvy enough to do it without that.
Ok, I was able to use the answer in Slava.K's comment to answer my question. Here's the final code below. I had to include (?!.*index) in the regex because this code was including itself index.views.js.
var context = require.context('.', true, /^(?!.*index).*\/(?!.*test).*\.js$/);
var moduleNames = _.chain(context.keys())
.map(function(key) {
return context(key)
})
.value();
module.exports = angular.module('myApp.demoApp.views', moduleNames).name;
I need a validator for a less file. I need to validate a single less file, at only syntax level.
I can't get the validator to follow the dependencies or detect if a mixin is declared or not. That is my real problem.
I have found a lot of less processors which fail because the file has dependencies that i can't provide in this point of the process.
npm package would be perfect.
Finally, I decided to use antlr4
Steps:
install antlr in the computer or add to the deployment process
get the grammar for your language (less is already done)
get the npm package to process the grammar
Use it in your app
The configuration is:
$ cd /usr/local/lib
$ sudo curl -O http://www.antlr.org/download/antlr-4.5-complete.jar
$ export CLASSPATH=".:/usr/local/lib/antlr-4.5-complete.jar:$CLASSPATH"
$ alias antlr4='java -jar /usr/local/lib/antlr-4.5-complete.jar'
$ alias grun='java org.antlr.v4.runtime.misc.TestRig
This info is directly from the web page.
Now you can get the grammar from https://github.com/antlr/grammars-v4
At this point you can generate the javascript version of your grammar
In my case, I created a directory, I downloaded the files and coded all my tests inside:
antlr4 -Dlanguage=JavaScript LessParser.g4
antlr4 -Dlanguage=JavaScript LessLexer.g4
This process generates a javascript file that you will use, but you need the antlr lib for use this files in your node program.
npm link antlr4
Ant now starts to code:
var antlr4 = require('antlr4/index');
var MyGrammarLexer = require('./LessLexer.js');
var MyGrammarParser = require('./LessParser.js');
var input = "html{ .hey(); color: #light-blue; background:#333}";
var chars = new antlr4.InputStream(input);
var lexer = new MyGrammarLexer.LessLexer(chars);
var tokens = new antlr4.CommonTokenStream(lexer);
var parser = new MyGrammarParser.LessParser(tokens);
parser.buildParseTrees = true;
var ErrorListener = antlr4.error.ErrorListener;
function CustomErrorListener() {
ErrorListener.call(this);
return this;
}
CustomErrorListener.prototype = Object.create(ErrorListener.prototype);
CustomErrorListener.prototype.constructor = CustomErrorListener;
CustomErrorListener.prototype.syntaxError = function(recognizer, offendingSymbol, line, column, msg, e) {
throw ('throw a simple exception');
};
parser.addErrorListener(new CustomErrorListener());
try{
var tree = parser.stylesheet();
} catch (e){
console.log('I catch you!!!')
}
The important parts in this code are the function lessLexer, lessParser, and parser.stylesheet(); This are different per grammar. The last one is difficult to know, but it is the point in the grammar that you want to validate. In my case, I get the file LessParser.g4, and you have the different node in the grammar in the definition:
parser grammar LessParser;
options { tokenVocab=LessLexer; }
stylesheet
: statement*
;
statement
: importDeclaration
| ruleset
| variableDeclaration ';'
| mixinDefinition
;
variableName
: AT variableName
| AT Identifier
;
commandStatement
: (expression+) mathStatement?
;
mathCharacter
: TIMES | PLUS | DIV | MINUS | PERC
;
In this case, you can validate the string like a stylesheet, statement, variableName ...
The last interesting point is the Error Validation, I use it to stop the validation in the first error, my case is very simple, but you can improve this point