Create factory for require node modules - javascript

I want to create factory/or similar approach based on the required OS windows/linux
for example if linux use
var isLinux = /^linux/.test(process.platform);
var isWin = /^win/.test(process.platform);
if(isLinux){
var spawn = require('child-process');
}{
elseif(isWin)
var spawn = require('cross-spawn')
}
module.export = spawn;
I know that the cross-spawn is also for windows...
my question is there a nicer way to write it in node (ver4.4) instead of just else if

switch(process.platform){
case 'windows':
// code
break;
case 'linux':
// code
break;
default:
return new Error('give us a real OS pls')
}
or
process.platform === 'windows' ?
//code windows :
//code linux
Here's some more food for thought. Alternatives to switch statements.

Related

Making commands case insensitive [Discord bot] in JS

I've seen a lot of people asking the same thing on Stack Overflow but I didn't see any of the cases in which people use the same type of code as me, like for instance I cannot use .toLowerCase().
if (message.substring(0, 1) == '+') {
var args = message.substring(1).split(' ');
var cmd = args[0];
args = args.splice(1);
switch(cmd) {
// Help command
case 'HELP':
bot.sendMessage({
to: channelID,
message: commandList
});
break;
Try putting toUpperCase() here
var cmd = args[0].toUpperCase();
or here:
switch(cmd.toUpperCase()) {
And if youre getting an error saying toUpperCase cant be put on undefined, then your code is broken somewhere here:
var args = message.substring(1).split(' ');
var cmd = args[0];
so try and see if your message is actually what you think it is.

Permissions error when running another JS AppleScript from another JSAppleScript

I am trying to separate out my .applescript files into different ones to tidy things up.
I have a JS AppleScript file called Test.applescript that tries to run the JS AppleScript file Group Tracks Dependency.applescript and what I want to do is pass in a parameter into the dependency script and get a return value out of it. (It creates an array of arrays of iTunes tracks).
Test.applescript
(function() {
var app = Application('iTunes');
app.includeStandardAdditions = true;
app.doShellScript('Group Tracks Dependency.applescript');
return "Done";
})();
// For quick logging
function log(obj) {
this.console.log(obj);
}
Group Tracks Dependency.applescript
(function(selection) {
return getGroupsOfTracks(selection);
function getGroupsOfTracks(originalTracksArray) {
if (originalTracksArray == null || originalTracksArray.length == 0)
return null;
var tracks = originalTracksArray.slice();
var groups = [];
while (true) {
var group = [];
group.push(tracks[0]);
tracks = tracks.slice(1);
while (true) {
if (!tracks[0]) break;
if (tracks[0].album() != group[0].album())
break;
if (tracks[0].artist() != group[0].artist())
break;
if (tracks[0].discNumber() != group[0].discNumber())
break;
group.push(tracks[0]);
tracks = tracks.slice(1);
}
groups.push(group);
if (!tracks[0]) break;
}
return groups;
}
})();
When I try to run the Test script I get this error (line 5 is the app.doShellScript line):
Error on line 5: Error: A privilege violation occurred.
Is there any way to get around this? I should also note that I want other people to be able to download these scripts and run them on their own iTunes libraries in the future (currently it's not user-friendly though).
If there's no way to get around this then would importing another JS AppleScript file work?
I think you may be fighting a battle that you can’t win using .doShellScript.
The Apple way is to use a Script Library as defined on https://developer.apple.com/library/mac/releasenotes/InterapplicationCommunication/RN-JavaScriptForAutomation/Articles/OSX10-11.html#//apple_ref/doc/uid/TP40014508-CH110-SW1
Unfortunately a script library has constraints where you can only pass simple variables.
Another way is to use require, which can be defined with code like https://github.com/dtinth/JXA-Cookbook/wiki/Importing-Scripts
I'm not sure what you are trying to accomplish, but this works for me using Script Editor 2.8.1 (183.1) on OSX 10.11.4:
Create a main JXA Script file
Create a JXA Script Library file
BOTH of these MUST be saved as compiled script files (.scpt)
It is INCORRECT that "Unfortunately a script library has constraints where you can only pass simple variables."
You can call any of the functions in the Script Library file from any JXA script.
In your MAIN script file, which I will call "Get iTunes Group Selection.scpt":
var app = Application('iTunes');
app.includeStandardAdditions = true;
var myLib = Library("My JXA Lib")
var selectionArr = app.selection() // ### Change as needed ###
var groupArr = myLib.getGroupsOfTracks(selectionArr)
groupArr
~~~~~~~~~~~~~~~~~~~~~
And then in a separate script file, saved as:
~/Library/Script Libraries/My JXA Lib.scpt
function getGroupsOfTracks(originalTracksArray) {
if (originalTracksArray == null || originalTracksArray.length == 0)
return null;
var tracks = originalTracksArray.slice();
var groups = [];
while (true) {
var group = [];
group.push(tracks[0]);
tracks = tracks.slice(1);
while (true) {
if (!tracks[0]) break;
if (tracks[0].album() != group[0].album())
break;
if (tracks[0].artist() != group[0].artist())
break;
if (tracks[0].discNumber() != group[0].discNumber())
break;
group.push(tracks[0]);
tracks = tracks.slice(1);
}
groups.push(group);
if (!tracks[0]) break;
}
return groups;
}
Well, it's been a few years...
I ran into errors with JXA and doShellScript when I tried to run with Application("Finder"). These errors went away when I instead ran the script from Application.currentApplication(). So for my script, I used const finder = Application("Finder") for Finder specific stuff, then const app = Application.currentApplication() for running the script.
For example:
//test1.scpt
function run() {
const app = Application.currentApplication()
app.includeStandardAdditions = true
app.doShellScript("osascript ~/Desktop/test2.scpt")
}
//test2.scpt
function run() {
const app = Application.currentApplication()
app.includeStandardAdditions = true
app.displayDialog("foo")
app.doShellScript("osascript -e 'display dialog \"bar\"'")
}
As expected, running test1.scpt gives me two dialogs: foo and `bar.

open a file with default program in node-webkit

I want to give the user any option he want to edit a file, how can I open a file with the default program of the specific file type? I need it to work with Windows and Linux but Mac option would be great too.
as PSkocik said, first detect the platform and get the command line :
function getCommandLine() {
switch (process.platform) {
case 'darwin' : return 'open';
case 'win32' : return 'start';
case 'win64' : return 'start';
default : return 'xdg-open';
}
}
second , execute the command line followed by the path
var exec = require('child_process').exec;
exec(getCommandLine() + ' ' + filePath);
You can use the open module:
npm install --save open
and then call it in your Node.js file:
const open = require('open');
open('my-file.txt');
This module already contains the logic to detect the operating system and it runs the default program that is associated to this file type by your system.
For file on a disk:
var nwGui = require('nw.gui');
nwGui.Shell.openItem("/path/to/my/file");
For remote files (eg web page):
var nwGui = require('nw.gui');
nwGui.Shell.openExternal("http://google.com/");
Detect the platform and use:
'start' on Windows
'open' on Macs
'xdg-open' on Linux
I am not sure if start used to work as is on earlier windows versions, however on windows 10 it doesn't work as indicated in the answer. It's first argument is the title of the window.
Furthermore the behavior between windows and linux is different. Windows "start" will exec and exit, under linux, xdg-open will wait.
This was the function that eventually worked for me on both platforms in a similar manner:
function getCommandLine() {
switch(process.platform) {
case 'darwin' :
return 'open';
default:
return 'xdg-open';
}
}
function openFileWithDefaultApp(file) {
/^win/.test(process.platform) ?
require("child_process").exec('start "" "' + file + '"') :
require("child_process").spawn(getCommandLine(), [file],
{detached: true, stdio: 'ignore'}).unref();
}
If you aim to script some kind of prompt with a default editor or simply chain files opening, you will have to wait until the program ends or fail.
Inspired from PSkocik and Khalid answers.
const {exec} = require('child_process');
let openFile=function(filePath,mute){
let command=(function() {
switch (process.platform) {
case 'darwin' : return 'open '+filePath+' && lsof -p $! +r 1 &>/dev/null';
case 'win32' :
case 'win64' : return 'start /wait '+filePath;
default : return 'xdg-open '+filePath+' && tail --pid=$! -f /dev/null';
}
})();
if(!mute)console.log(command);
let child=exec(command);
if(!mute)child.stdout.pipe(process.stdout);
return new function(){
this.on=function(type,callback){
if(type==='data')child.stdout.on('data',callback);
else if(type==='error')child.stderr.on('data',callback);
else child.on('exit',callback);
return this;
};
this.toPromise=function(){
return new Promise((then,fail)=>{
let out=[];
this.on('data',d=>out.push(d))
.on('error',err=>fail(err))
.on('exit',()=>then(out));
});
};
}();
};
use :
openFile('path/to/some_text.txt')
.on('data',data=>{
console.log('output :'+data);
})
.on('error',err=>{
console.log('error :'+err);
})
.on('exit',()=>{
console.log('done');
});
or :
openFile('path/to/some_text.txt').toPromise()
.then(output=>{
console.log('done output :'+output.join('\n'));
}).catch(err=>{
console.log('error :'+err);
});
PS : Let me know if it waits for other sytems than winXX ( Inspired from Rauno Palosaari post but not tested yet ).

Identifying the File System Root with Node.js

I'm doing a basic operation where I start from a given directory, and I traverse up the filesystem until I hit the root. On Linux/Mac, the root is obviously / and on Windows it can be C:\ or another drive letter of course. My question is whether or not there is a way for Node.js to identify what the root directory of the filesystem is.
Currently, I'm resorting to simply checking the last directory against path.normalize(dir + "/../") to see if it stops changing. Is there a process property/method out there? Maybe a module?
Another one, using path.parse.
const path = require('path')
const getRootDir = () => path.parse(process.cwd()).root
Would this not work?
var path = require("path");
var os = require("os");
var root = (os.platform == "win32") ? process.cwd().split(path.sep)[0] : "/"
There's nothing special that Node.js needs to do, the answer is a simple regex:
/^([^\\/]*[\\/]).*/.test(process.cwd())
var root = RegExp.$1;
That should get the root from the CWD for both Windows and Linux.
I think that the simplest way to do this is simply to check if path.dirname returns the same path as the input path: if it does, then it is the root.
const { dirname } = require('path');
// On Linux, MacOS or other POSIX-like file-systems:
dirname("/") === "/" // -> true
// On Windows:
dirname("C:\\") === "C:\\" // -> true
Here is an example output from Node.js (v14) REPL. Note that path will automatically resolve to path.win32 or path.posix depending on your system, so you don't have to worry about platform compatability:
> path.posix.dirname("/") === "/"
true
> path.win32.dirname("C:\\") === "C:\\"
true
So if you want a utility function for it, you can do something like:
const isRoot = (path) => dirname(path) === path
… or if you want to traverse parents until you hit the root, you can do something like:
function parent(path) {
const parentPath = dirname(path);
return parentPath === path ? null : parentPath;
}
let path = process.cwd();
do {
console.log(path);
} while (path = parent(path))

Batch file called from Javascript/XPCOM doesn't show command prompt window

I am calling a batch file from Javascript in this fashion:
function runBatch(){
var exe = Components.classes['#mozilla.org/file/local;1'].createInstance(Components.interfaces.nsILocalFile);
exe.initWithPath("C:\\test.bat");
var run = Components.classes['#mozilla.org/process/util;1'].createInstance(Components.interfaces.nsIProcess);
run.init(exe);
var parameters = ["hi"];
run.run(false, parameters,parameters.length);
}
my test batch file is:
echo on
echo %1
pause
exit
Each time I call a batch file, however, the command prompt is not displayed, as it would be if I simply ran the batch file from the desktop. How can I remedy this and display a command prompt for the batch file?
Edit
To be clear, the cmd.exe process is launched - I can see it in the task bar. But no window gets displayed. This snippet behaves similarly:
function runCmd(){
var exe = Components.classes['#mozilla.org/file/local;1'].createInstance(Components.interfaces.nsILocalFile);
exe.initWithPath("C:\\WINDOWS\\system32\\cmd.exe");
var run = Components.classes['#mozilla.org/process/util;1'].createInstance(Components.interfaces.nsIProcess);
run.init(exe);
run.run(false, null,0);
}
The only solution I've heard so far (that should work, although I haven't done it yet, comes from Mook in the Mozilla xulrunner IRC channel:
create a temporary batch file, writing in the batch file to call and arguments to pass it. then execute the temporary batch file.
e.g psuedocode:
f = fopen("temp.bat");
fprintf(f, "other.bat 1 2 3 4 5");
fclose(f);
exec("temp.bat");
not very elegant but it should work.
Did you try using the launch method of nsiLocalFile?
function runBatch(){
var exe = Components.classes['#mozilla.org/file/local;1'].createInstance(Components.interfaces.nsILocalFile);
exe.initWithPath("C:\\test.bat");
exe.launch();
}
This should have "the same effect as if you double-clicked the file."
This code snippet seems to work fine. Of course, you have to change D:\Windows\system32\ to path to cmd.exe in your operation system.
const FileFactory = new Components.Constructor("#mozilla.org/file/local;1","nsILocalFile","initWithPath");
var str_LocalProgram = "D:\\Windows\\system32\\cmd.exe";
var obj_Program = new FileFactory(str_LocalProgram);
var process = Components.classes["#mozilla.org/process/util;1"].createInstance(Components.interfaces.nsIProcess);
process.init(obj_Program);
var args = ["/C", "regedit.exe"];
process.run(true, args, args.length);
I had to launch a batch file and pass in an argument. This is how I did it:
let file = uri.QueryInterface(Components.interfaces.nsIFileURL).file;
let run = Components.classes['#mozilla.org/process/util;1']
.createInstance(Components.interfaces.nsIProcess);
let path = file.path;
if(file.exists())
{
// quick security check
if(file.isExecutable())
{
// show error message
return;
}
let localfile = file.QueryInterface(Components.interfaces.nsILocalFile);
if(localfile != null)
{
if (app == "app1")
{
localfile.initWithPath("C:\\app1.bat");
}
else
{
localfile.initWithPath("C:\\app2.bat");
}
run.init(localfile);
var parameters = [path];
run.run(false, parameters, parameters.length);
}
else
{
// show error message
}
}
else
{
// show error message
}
and in my Window batch file I did:
#ECHO OFF
START "application.exe" %1
using START, allowed me to launch the application and close the command line window
You are doing right but repair this:
function runBatch(){
var exe = Components.classes['#mozilla.org/file/local;1'].createInstance(Components.interfaces.nsILocalFile);
exe.initWithPath("***C:\ \test.bat***");
var run = Components.classes['#mozilla.org/process/util;1'].createInstance(Components.interfaces.nsIProcess);
run.init(exe);
var parameters = ["hi"];
run.run(false, parameters,parameters.length);
}
If you do this???
function runBatch(){
var exe = Components.classes['#mozilla.org/file/local;1'].createInstance(Components.interfaces.nsILocalFile);
exe.initWithPath("***C:\test.bat***");
var run = Components.classes['#mozilla.org/process/util;1'].createInstance(Components.interfaces.nsIProcess);
run.init(exe);
var parameters = ["hi"];
run.run(false, parameters,parameters.length);
}
An put #echo off at init???
Thanks
Pfft, very ugly code..
A much nicer trick is to use Win.com to spawn a 16bit subsystem of the command prompt.
Win.com will send the console to the right virtual terminal, showing you the output.
var lPath = getWorkingDir.path + "\\..\\..\\WINDOWS\\system32\\win.com";
lFile.initWithPath(lPath);
var process = Components.classes["#mozilla.org/process/util;1"].createInstance(Components.interfaces.nsIProcess);
process.init(lFile);
var args = ["cmd.exe"];
process.run(false, args, args.length);
Nicer, and works :)
For Linux:
<script>
function callLight2()
{
netscape.security.PrivilegeManager.enablePrivilege(
'UniversalXPConnect'
);
var exe = Components.classes['#mozilla.org/file/local;1'].createInstance(Components.interfaces.nsILocalFile);
// exe.initWithPath(C:\\Windows\\system32\\cmd.exe"");
exe.initWithPath("/usr/bin/gnome-terminal");
var run = Components.classes['#mozilla.org/process/util;1'].createInstance(Components.interfaces.nsIProcess);
run.init(exe);
var parameters = ["-e", "/usr/bin/ip_connect_up.sh 2 2 3 4 5 6"];
// var parameters = ["/C", "regedit.exe"];
// var parameters = ["hi"];
run.run(true, parameters,parameters.length);
}
</script>
start

Categories

Resources