Require nodejs "child_process" with TypeScript, SystemJS and Electron
Asked Answered
J

4

24

I'm working on a simple nodejs electron (formerly known as atom shell) project. I'm writing it using angular 2, using the project the same project setup as they recommend in the documentation for typescript:

tsc:

{
  "compilerOptions": {
    "target": "es5",
    "module": "system",
    "moduleResolution": "node",
    "sourceMap": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "removeComments": false,
    "noImplicitAny": false
  },
  "exclude": [
  "node_modules",
  "typings/main",
  "typings/main.d.ts"
  ]
}

I need to run a command, I found out that I can do it with node "child_process". I couldn't find anyway for me to "import" or "require" it while having its type used from the node.d.ts file. I have found the "child_process" interface in the node.d.ts file which suits my need, this is how it looking in the node.d.ts file:

    declare module "child_process" {
    import * as events from "events";
    import * as stream from "stream";

    export interface ChildProcess extends events.EventEmitter {
        stdin:  stream.Writable;
        stdout: stream.Readable;
        stderr: stream.Readable;
        pid: number;
        kill(signal?: string): void;
        send(message: any, sendHandle?: any): void;
        disconnect(): void;
        unref(): void;
    }

    export function spawn(command: string, args?: string[], options?: {
        cwd?: string;
        stdio?: any;
        custom?: any;
        env?: any;
        detached?: boolean;
    }): ChildProcess;
    export function exec(command: string, options: {
        cwd?: string;
        stdio?: any;
        customFds?: any;
        env?: any;
        encoding?: string;
        timeout?: number;
        maxBuffer?: number;
        killSignal?: string;
    }, callback?: (error: Error, stdout: Buffer, stderr: Buffer) =>void ): ChildProcess;
    export function exec(command: string, callback?: (error: Error, stdout: Buffer, stderr: Buffer) =>void ): ChildProcess;
    export function execFile(file: string,
        callback?: (error: Error, stdout: Buffer, stderr: Buffer) =>void ): ChildProcess;
    export function execFile(file: string, args?: string[],
        callback?: (error: Error, stdout: Buffer, stderr: Buffer) =>void ): ChildProcess;
    export function execFile(file: string, args?: string[], options?: {
        cwd?: string;
        stdio?: any;
        customFds?: any;
        env?: any;
        encoding?: string;
        timeout?: number;
        maxBuffer?: number;
        killSignal?: string;
    }, callback?: (error: Error, stdout: Buffer, stderr: Buffer) =>void ): ChildProcess;
    export function fork(modulePath: string, args?: string[], options?: {
        cwd?: string;
        env?: any;
        execPath?: string;
        execArgv?: string[];
        silent?: boolean;
        uid?: number;
        gid?: number;
    }): ChildProcess;
    export function spawnSync(command: string, args?: string[], options?: {
        cwd?: string;
        input?: string | Buffer;
        stdio?: any;
        env?: any;
        uid?: number;
        gid?: number;
        timeout?: number;
        maxBuffer?: number;
        killSignal?: string;
        encoding?: string;
    }): {
        pid: number;
        output: string[];
        stdout: string | Buffer;
        stderr: string | Buffer;
        status: number;
        signal: string;
        error: Error;
    };
    export function execSync(command: string, options?: {
        cwd?: string;
        input?: string|Buffer;
        stdio?: any;
        env?: any;
        uid?: number;
        gid?: number;
        timeout?: number;
        maxBuffer?: number;
        killSignal?: string;
        encoding?: string;
    }): string | Buffer;
    export function execFileSync(command: string, args?: string[], options?: {
        cwd?: string;
        input?: string|Buffer;
        stdio?: any;
        env?: any;
        uid?: number;
        gid?: number;
        timeout?: number;
        maxBuffer?: number;
        killSignal?: string;
        encoding?: string;
    }): string | Buffer;
}

but I can only (as I know of) get this type only by using import:

import * as child_process from 'child_process'; 

Only problem is that when I do this, my app cant load and I get the following error in the console:

GET file:///C:/angular2Samples/NGW-electron-VS%20-%20TEMP/child_process net::ERR_FILE_NOT_FOUND

For now, im getting my way around by using:

var child_process = require('child_process');

but I couldn't find anyway to add the type information to this var:

var child_process : I_CANT_PUT_ANY_CHILD_PROCESS_TYPE_HERE = require('child_process');

Any ideas on how I can get the child_process (or any other declared node modules that arent public interface that I can state after ":" operator) with type information?

Thanks alot in advance for any help and explanations :)

UPDATE ------------------------------------------------------------------

As tenbits suggested I have added the reference as follows to the top of the file: ///

and used the import statment you said, but didnt chage my module loader. it still didnt work with the same error as expected. Im not feeling very comfortable about changing the module system, as my project uses angular 2 and their docs and some of their guides said that new projects that has no former prefernce to this matter (I am very new to the module loaders scene and im not fully understanding how it works yet). When I tried to change it I got some errors regarding angular 2 stuffs which I dont have enough time to get into at the moment. Shouldn't there be a way to this without changing the module loader? by glancing at the systemjs site it says at the start that it supports commonjs modules: Systemjs doc

I would really appriciate a solution that doesn't change the module system, or maybe a more depth explanition about what's going on and which approaches to these kind of module loading problems exists out there

Jillion answered 11/4, 2016 at 11:2 Comment(1)
yep, TS makes everything more difficult.Emmer
E
50

Ok, after some research #L138 I have found the solution

You can use import as before

import * as child from 'child_process';

var foo: child.ChildProcess = child.exec('foo.sh');
console.log(typeof foo.on);

But you should configure SystemJS to map the module to NodeJS.

System.config({
  map: {
    'child_process': '@node/child_process'
  }
});

That's it!

Esque answered 11/4, 2016 at 13:8 Comment(15)
I'm not quite got it: Have you tried import child = require('child_process'); instead of var child = require('child_process');? Do you have the same result as with var?Esque
Yes, exactly the same (notice that I didnt change the module to commonjs as you suggested, the reason for that is noted in my update above)Jillion
Thanks again for your new suggestion. Im afraid it didn't work. I copied your last version of the System.config and changed my code to use the import statement with require as you suggested in the first version of your andwer and still got the same xhr error. I also tried: import * as child from "child_process"; That didn't work either with same error. I also tried doing it with the /// reference at the top to the node.d.ts but as I expected it didn't change anythingJillion
Maybe also rename the title of the question to "Require nodejs “child_process” with typescript, systemjs and electron".Esque
Glad it was helpful, actually I was not familiar with SystemJS, only the TypeScript and Electron, but I supposed the problem was, that SystemJS was loading nodejs module as regular browser dependency. So at first I suggested to map it somehow to CommonJS module name pattern. When that no helped I just looked into the source code to understand how actually the mapping works, and saw that it was this @node prefix required. Yes, It should be documented, but you can always take a look into the source codes, so you can understand how the library was "designed"). CheersEsque
How to achieve this with commonJs module?Keelson
@Aish123, with @node/ you can only load the core node modules, see the line and notice the nodeCoreModules array. What definitely works: import * as baz from 'node_modules/foo/x.js'; And for TypeScript you have to create empty module definition to get this compiled.Esque
@tenbits, sorry i unable to understand what you mean as empty module definition. I have .d.ts library file which calls the child_process. I am getting error as the "cannot find module 'child_process '". Could you please elaborate what you mean so that i can try.Keelson
@Aish123, ah, you get the Typescript error at compile time. You have to install Node Typings node.d.ts. You do this with tsd. Install it first: npm install tsd -g. Then in your project directory run the command tsd install node. This will install node.d.ts in typings directory. And will create tsconfig.json.Esque
well i have done that as well. Included the ref and added import {exec} from 'child_process';. Still getting the error Cannot resolve module 'child_process'Keelson
@Aish123, make in tsconfig.json you have the property "moduleResolution": "node",. Maybe also try to install node typings with save falg tds install node --save. Please, give also the feedback)Esque
sorry for the delayed response. i am still getting error and the reason in "child_process" module is checked in node_modules folder rather than the reference node.d.ts file. I am unsure how to import it in my fileKeelson
Hm, actually "child_process" is the core module of nodejs. How it can be in "node_modules" folder? And there is no 3rd party child_process modules: May be you should start the new question and explain your problem in details, so that we move the discussion in there?Esque
i have modified my query in the link #36914177Keelson
i dont have system.config.js can you help me out how to solve this problem?Jarboe
B
10

If the error message is Cannot find module 'child_process' or its corresponding type declarations the answer is npm install @types/node

Bilicki answered 4/10, 2020 at 15:56 Comment(0)
A
5

For me it worked with the callback to display the results.

import * as child from 'child_process';

 var foo: child.ChildProcess = child.exec('dir', (error: string, stdout: string, stderr: string) => {
            console.log(stdout);      
        });

I didn't add any mappings in SystemJS as I dont have any such configuration in the node application

Acidulate answered 22/9, 2017 at 10:53 Comment(0)
H
4

Me & Bing chat:

import { exec } from "child_process";

function openBrowser(url) {
  // Get the operating system
  const os = process.platform;

  // Choose the command based on the operating system
  let cmd;
  if (os === "win32") {
    // Windows
    cmd = `start ${url}`;
  } else if (os === "darwin") {
    // Mac OS
    cmd = `open ${url}`;
  } else if (os === "linux") {
    // Linux
    cmd = `xdg-open ${url}`;
  } else {
    // Unsupported OS
    throw new Error("Unsupported operating system");
  }

  // Execute the command
  exec(cmd, (error, stdout, stderr) => {
    if (error) {
      console.error(`Failed to open browser: ${error.message}`);
      return;
    }
    if (stderr) {
      console.error(`Error: ${stderr}`);
      return;
    }
    console.log(`Opened browser: ${stdout}`);
  });
}

// Example usage
openBrowser("https://www.google.com");
Helga answered 14/6, 2023 at 13:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.