getopt-like CLI Options in node.js

Parsing CLI options with getopt-ish configuration

#!/usr/bin/env node

function argvAddValue(ret, id, val) {
    if (ret[id] === undefined) {
        ret[id] = val;
        return;
    }

    if (!Array.isArray(ret[id])) ret[id] = [ ret[id] ];
    ret[id].push(val);
}

function argvParser(options, argv) {
    if (!argv) argv = process.argv;

    const { length } = argv;

    const sym = Symbol.for('input');
    const ret = {};

    for(let idx = 2, needValue = false; idx < length; idx++){
        const arg = argv[idx];

        if(arg[0] !== '\-') {
            if (ret[sym] === undefined) ret[sym] = [];
            if (!needValue) ret[sym].push(arg);
            continue;
        }

        for(const key of options){
            let token = key;

            needValue = Boolean(token.match(/(:$)/));
            if (needValue) token = token.replace(/:$/, '');

            const argIsLongOpt = Boolean(arg.match(/^\-\-/));
            const baseToken = `(^` + token.replace(/\-/g, '\\-');
            const regexp = argIsLongOpt
                ? new RegExp(baseToken + '(?:=|\$))')
                : new RegExp(baseToken + ')');
            const hasMatch = arg.match(regexp);

            if (!hasMatch) continue;

            // take long options name or first char of the token
            const id = token.match(/(?:^|\|)\-\-(.*?)(?:=|$)/)?.[1] ?? arg[1];

            if (!needValue) {
                argvAddValue(ret, id, true);
                break;
            }

            // no longer necessary, revert now
            needValue = false;

            if (argIsLongOpt) {
                const afterEq = arg.match(/=(.*$)/);
                if (afterEq) {
                    const value = afterEq[1];
                    argvAddValue(ret, id, value);
                    break;
                }
            }

            if (!argIsLongOpt && arg.length > 2) {
                const value = arg.replace(hasMatch[1], '');
                argvAddValue(ret, id, value);
                break;
            }

            const value = argv[++idx];
            argvAddValue(ret, id, value);
        }
    }

    return ret;
}

/* ***************************** EXAMPLE ************************************ */
const parsed = argvParser([
    '-c',  // short opt switch
    '-b:', // short opt with value
    '--long',      // long opt switch
    '--long-arg:', // long opt with option
    '-v|--verbose',  // short and long opt switch
    '-e|--example:', // short and long opt with value
]);

console.log(parsed);
console.table(parsed);
console.table(parsed[Symbol.for('input')]);

/*******************************************************************************
 * INPUT:
 * ./argvParser.js          \
 *     file1                \
 *     -v                   \
 *     -bIPv4               \
 *     --long               \
 *     --long-arg arg1      \
 *     --long-arg=arg2      \
 *     -e example           \
 *     file2
 *
 * OUTPUT:
 * {
 *   verbose: true,
 *   b: 'IPv4',
 *   long: true,
 *   'long-arg': [ 'arg1', 'arg2' ],
 *   example: 'example',
 *   [Symbol(input)]: [ 'file1', 'file2' ]
 * }
 * ┌──────────┬────────┬────────┬───────────┐
 * │ (index)  │   0    │   1    │  Values   │
 * ├──────────┼────────┼────────┼───────────┤
 * │ verbose  │        │        │   true    │
 * │    b     │        │        │  'IPv4'   │
 * │   long   │        │        │   true    │
 * │ long-arg │ 'arg1' │ 'arg2' │           │
 * │ example  │        │        │ 'example' │
 * └──────────┴────────┴────────┴───────────┘
 * ┌─────────┬─────────┐
 * │ (index) │ Values  │
 * ├─────────┼─────────┤
 * │    0    │ 'file1' │
 * │    1    │ 'file2' │
 * └─────────┴─────────┘
 * */

Integrate vpckg and cmake-js

I missed how easy to install system wide library using apt, pkg, or brew. Something that missing in Windows. Fortunately, vcpkg exists. Unfortunately, it's not as easy as the other system wide package manager.

I successfully setup vcpkg to install system wide libraries, with these steps:

NOTE: Visual Studio must be already installed.

  1. Don't use Developer PowerShell from Visual Studio installation. Just use the general PowerShell.

    Developer PowerShell from Visual Studio will use embedded vcpkg which doesn't support system wide installation.

  2. Install vcpkg

    git clone https://github.com/microsoft/vcpkg "<path-to-vcpkg>"
    cd "<path-to-vcpkg>"
    .\bootstrap-vcpkg.bat
    .\vcpkg integrate install
  3. Edit User Profile to set environment variables

    You can edit the User profile in PowerShell with this command

    notepad $profile.CurrentUserCurrentHost

    then add these lines at the end of file

    # set vcpkg environment variable and path 
    $env:VCPKG_ROOT="<path-to-vcpkg>"
    $env:Path += ";$env:VCPKG_ROOT"
    
    # to set MSVC environment variables for x86_amd64 platform. 
    # This step assumes you installed Visual Studio 2022 Community edition for x86_64 platform
    & "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat" x86_amd64

    restart or open new PowerShell session.

    to set environment variables for current session, you can reload the profile with this command

    . $PROFILE
  4. Edit your CMakeLists.txt

    Put these lines before the first project()

    if (WIN32)
        if (NOT DEFINED ENV{VCPKG_ROOT})
            message(FATAL_ERROR "$ENV{VCPKG_ROOT}: Env VCPKG_ROOT needs to be set!")
        endif ()
    
        set(CMAKE_TOOLCHAIN_FILE "$ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake")
    endif ()

    at this step, Env variable VCPKG_ROOT must've been set as this is the necessary condition for our cmake to work.

  5. Now, you can install system wide libraries with vcpkg and use find_package, find_path, and find_library in CMakeLists.txt

    Example:

    • Install openssl, libssh2, and zlib

      vcpkg install openssl libssh2 zlib
    • use installed libs in CMakeLists.txt

      find_package(OpenSSL REQUIRED)
      target_link_libraries("${PROJECT_NAME}" PRIVATE OpenSSL::SSL OpenSSL::Crypto)
      
      find_package(ZLIB REQUIRED)
      target_link_libraries("${PROJECT_NAME}" PRIVATE ZLIB::ZLIB)
      
      find_library(LIBSSH2_LIBRARY NAMES ssh2 libssh2)
      find_path(LIBSSH2_INCLUDE_DIR NAMES libssh2.h)
      target_link_libraries("${PROJECT_NAME}" PRIVATE "${LIBSSH2_LIBRARY}")
      target_include_directories("${PROJECT_NAME}" PRIVATE "${LIBSSH2_INCLUDE_DIR}")

SDDM greeter black screen after upgrade to Kubuntu 25.10

SDDM greeter always gives No Signal after upgrading from 25.04 to 25.10

At first, I think it's nvidia driver issue. So, I changed these files below:

  1. /ets/default/grub

    GRUB_CMDLINE_LINUX_DEFAULT="quiet nvidia_drm.modeset=1 nvidia_drm.fb_dev=1 resume=UUID=3fd0f470-9846-4bf7-a286-390f9c99b275"
  2. /etc/modprobe.d/nvidia-graphics-drivers-kms.conf

    options nvidia_drm modeset=1
    options nvidia_drm fbdev=1

    both number 1 and 2, cause I think it was nvida driver 580 has problem with wayland

  3. /etc/modprobe.d/blacklist.conf

    blacklist nouveau
    options nouveau modeset=0

    cause I thought the nouveau driver also runs when nvidia driver is already running.

    NOTE: After number 3, I ran

    sudo update-initramfs -u
    sudo update-grub

Nothing works. The SDDM still gave me No Signal

Then I found strange things, I typed my password during black screen then I was able to go into desktop.

Turns out, SDDM greeter runs on X11 and I don't have xorg installed. The steps are as follows,

  1. Install xorg

    sudo apt install xorg -y
  2. Populate /etc/X11/xorg.conf with nvidia-xconfig

    sudo nvidia-xconfig

Just like that, and the greeter was able to display on the next reboot.

Welp, it's weird because previously on Kubuntu 25.04 everything went well. Perhaps during upgrade process via do-release-upgrade, somehow xorg was uninstalled

UPDATE

It's indeed that xorg was removed by 25.10. But it's said that it's only for gnome, and even though I use KDE it's still got removed.