scharf
August 9, 2016, 5:22pm
1
I have a simple but long running task that I want to run as background process using child_process.fork
. The forked process needs to import some node modules (e.g. clone
).
I decided to put the script into the private
directory. The dependencies are also used in the normal code, so they should be in the node_modules
folder. However, if I run it on galaxy, I get an Error:
Error: Cannot find module 'clone'
.
My questions:
Where to best place the script (is private
a good place?)?
What to do so that the script finds node_modules
?
Hi @scharf , I have the same pb. If you have found a solution, can you share it please?
scharf
October 18, 2018, 1:18pm
3
Here is the (typescript) code that I use:
import { Meteor } from 'meteor/meteor';
export interface SpawnProcessResult {
cmd: string;
args: string[];
error?: any;
stdout: string;
stderr: string;
code?: number; // return code
}
export interface SpawnProcessCallback {
(err: Meteor.Error, result?: SpawnProcessResult): void;
}
function unquote(str: string): string {
return str.replace(/\\./g, function(s) {
switch (s[1]) {
case 'n':
return '\n';
case 't':
return '\t';
case 'r':
return '\r';
}
return s[1];
});
}
/**
* Splits an argument string so that it can contain quotes etc
* @param args
* @returns {string[]}
*/
export function splitArgs(args: string): string[] {
const arglist = args.split(/(\s+|"(?:[^"\\]+|\\.)*"|'(?:[^'\\]+|\\.)*')/);
const result: string[] = [];
for (let i = 0; i < arglist.length; i += 2) {
const a0 = arglist[i];
// this is a space separated arg
if (a0) {
result.push(a0);
}
// this is a quoted arg
const a1 = (arglist[i + 1] || '').trim();
if (a1) {
result.push(unquote(a1.slice(1, -1)));
}
}
return result;
}
export function callSpawnProcess(cmd: string, args: string[], cb: SpawnProcessCallback) {
Meteor.call('spawnProcess', cmd, args, cb);
}
and on the server I have:
import { Meteor } from 'meteor/meteor';
import { hasPermissionTo } from 'YOUR_PERMISSION_SYSTEM';
import { SpawnProcessResult } from './SpawnProcess';
import Future = require('fibers/future');
const spawn = require('child_process').spawn;
export function spawnProcess(cmd: string, args: string[]): SpawnProcessResult {
const future = new Future<SpawnProcessResult>();
const result: SpawnProcessResult = {
cmd,
args,
stdout: '',
stderr: '',
};
const prog = spawn(cmd, args, { shell: '/bin/sh' });
prog.stdout.on('data', (data: string) => {
result.stdout += data;
});
prog.stderr.on('data', (data: string) => {
result.stderr += data;
});
prog.on('error', (error: any) => {
result.error = error;
});
prog.on('close', (code: number) => {
result.code = code;
future.return(result);
});
return future.wait();
}
Meteor.methods({
spawnProcess(this: MethodThis, cmd: string, args: string[]): SpawnProcessResult {
// VERY IMPORTANT to check permissions here using your permission system
if (!hasPermissionTo('permToExecuteCommandsOnServer')) {
throw new Meteor.Error(403, `User ${this.userId} cannot execute commands!`);
}
// see https://gist.github.com/possibilities/3443021#unblocking-meteormethods
this.unblock();
return spawnProcess(cmd, args);
},
});
scharf
October 18, 2018, 1:20pm
4
Hmm, not sure if it solves the node_modules
problem, because I run only non-node processes
Indeed! In your first post, you were using child_process.fork()
to run a NodeJS script. child_process.spawn()
doesn’t fit my needs. Thank you anyway @scharf for answering me.
I managed to solve this, the solution is here:
I managed to solve the node_modules issue.
The problem is that meteor puts the node_modules in a folder called “npm”. So, the paths where node looks for the modules are all wrong. Node looks for node_modules folder in the app directory and all its parents but will never look for it in npm/node modules. The solution was just to change the paths like this, I added this on the top of the scripts to alter the paths:
let cache = require('module')._cache;
let paths = cache[Object.keys(cache)[0]].pat…
1 Like