Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Make node application executable in a NX workspace

I really like the structure of a NX workspace, and that lead me to start using it when building a new CLI project.

I started with creating a @nrwl/node:application but i currently is having some issues making it executable.

I believe this is not a problem with NX itself, but i can't add a shebang #!/usr/bin/env node in the main.ts file since the tsc transpiler will complain.

Module parse failed: Unexpected character '#' (1:0) File was processed
with these loaders:  * ./node_modules/ts-loader/index.js

I have added the "bin": {"cli": "main.js"} property in my package.json file but if i run the main.js file without the shebang i will get this error:

line 1: syntax error near unexpected token `('
C:\Users\*\AppData\Roaming\npm/node_modules/*/dist/apps/*/main.js: line 1: `(function(e, a) { for(var i in a) e[i] = a[i]; }(exports, /******/ (function(modules) { // webpackBootstrap

Is there any smart way of solving this?

Steps to reproduce:

  1. npx create-nx-workspace@latest cli-workspace --preset empty --cli nx --nx-cloud false
  2. cd cli-workspace
  3. npm install -D @nrwl/node
  4. nx generate @nrwl/node:application my-cli
  5. Add #!/usr/bin/env node to the top of the main.ts file
  6. npm start
like image 322
Stefan Karlsson Avatar asked Aug 11 '26 14:08

Stefan Karlsson


1 Answers

I've had the same problem. I could solve this by using the @nx-extend/gcp-functions:build builder to build the application, then I've created a custom executor to install the application as a CLI tool.

import { ExecutorContext } from '@nrwl/devkit';
import { exec } from 'child_process';
import { promisify } from 'util';
import * as fs from 'fs';

export interface NodeInstallOptions {
    buildPath: string;
    appName: string;
}

export default async function nodeInstallExecutor(
    options: NodeInstallOptions,
    context: ExecutorContext
) {
    let success = true;

    const mainPath = `${options.buildPath}/main.js`;

    console.log('Building library...');
    await runBashLine(success, `nx build ${options.appName}`);

    console.log('Adding shebang line to main.js...');
    const file = fs.readFileSync(mainPath, 'utf8');
    const first_line = file.split("\n")[0];
    if (first_line !== '#!/usr/bin/env node') {
        var data = "#!/usr/bin/env node\n\n";
        data += fs.readFileSync(mainPath, 'utf8');
        fs.writeFileSync(mainPath, data);
    }

    console.log("Packing library...")
    await runBashLine(success, `cd ${options.buildPath}; npm pack`);

    console.log('Installing library...');
    await runBashLine(success, `npm install -g ${options.buildPath}/*.tgz`);

    return { success };
}

async function runBashLine(success: boolean, line: string) {
    const { stdout, stderr } = await promisify(exec)(line);
    console.log(stdout);
    console.error(stderr);

    success = success && !stderr;
}
like image 193
Remco Goyvaerts Avatar answered Aug 14 '26 09:08

Remco Goyvaerts



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!