i’ve been using Nushell for a few months now as my daily and only shell. i love it and could not get back to POSIX shells like bash or fish, that’d be a real pain 😭

i could talk about why it’s so great all day, but in this blog post, i’d like to focus on a small detail i find really cool 😏

-------------------------------------------

i’ve always looked for a Makefile alternative

this is the main observation. i find the syntax of Makefiles very confusing

  • based on bash which is not great in itself
  • but not totally bash so it very often feels confusing and frustrating…

cmake, i hear you say?

well, cmake is definitely better!
but, in the end, it’s just a wrapper around make => it only gives a bit of syntactic sugar but produces regular Makefiles which are then run with make

other tools?

they are not as common most of the time, poorly documented, etc, etc, …

-------------------------------------------

here comes Nushell

in Nushell, one can write scripts called modules.
modules are a bit like regular scripts but integrate in the shell much better

  • better help pages
  • custom completion

Note
the following will work with the latest revision of nushell only.
not the latest release, the latest revision!

in fact, one would need to have installed a version of nushell after nushell/nushell#9066, e.g. from any branch / commit based on a2a346e39.

the idea here is to define a toolkit module to replace any Makefile and thus only have Nushell as the main dependency… and we are already in Nushell! so that’s a free dependency :partying:

where to put the toolkit module?

the main answer to this is “in the root of the project”!
and there are two ways to do that:

  • as a regular file module: toolkit.nu
  • as a directory module: toolkit/mod.nu (only available after nushell/nushell#9066)

how to use this toolkit?

once in the root of your project, simply run

use toolkit.nu  # with a regular module

or

use toolkit/  # with a directory module

an example of toolkit

let’s have a look maybe at the most Nushelly toolkit of all: the nushell toolkit.

Note
in this section, i use the toolkit of the latest revision of nushell AT THE TIME I WRITE THIS.
it might have changed now 😉

Note
Nushell is written in Rust and thus its toolkit wraps some cargo commands.

the toolkit exposes the following API


fmt [
    --check: bool
    --verbose: bool
];

clippy [
    --verbose: bool
];

test [
    --fast: bool
];

test stdlib [];

check pr [
    --fast: bool
];

setup-git-hooks [];

fmt, clippy and the test commands are here to check the source base for

  • bad formatting
  • lint errors
  • failing tests, both for the main source base in Rust and the standard library written in Nushell

check pr wraps them all and says if a PR will be valid or not.

finally setup-git-hooks… well sets git hooks up 😉

Note
more information is available by running help toolkit ...

a nice addition: automatic loading of the toolkit with hooks

in order to have any toolkit available at anytime when i enter a directory containing a toolkit module, i’ve written a little hook.

Note
in Nushell, a hook is a piece of code which runs on some condition, e.g. changing directory, displaying output, …

for this, i’ve used the env_change.PWD hook, which activates when the environment changes, more precisely the PWD variable, i.e. the current directory!

the hook will consist of a list of {condition: closure, code: closure} records.

  • the first one always runs and deactivates any previous toolkit
{ code: "hide toolkit" }
  • next, we activate the toolkit if we find one.
    the condition part of the hook record has access to the before and after directories, we can thus check if there is a toolkit.nu or a toolkit/mod.nu inside the after directory!
{|_, after| $after | path join 'toolkit.nu' | path exists }

or

{|_, after| $after | path join 'toolkit' 'mod.nu' | path exists }

and the actual code is then respectively

use toolkit.nu

or

use toolkit/

to wrap this all in a single command, i’ve written the following


def "env-change pwd toolkit" [
  --directory: bool
] {{
    condition: (if $directory {
        {|_, after| $after | path join 'toolkit' 'mod.nu' | path exists }
    } else {
        {|_, after| $after | path join 'toolkit.nu' | path exists }
    })
    code: ([
        "print -n $'(ansi default_underline)(ansi default_bold)toolkit(ansi reset) module (ansi yellow_italic)detected(ansi reset)... '"
        $"use (if $directory { 'toolkit/' } else { 'toolkit.nu' })"
        "print $'(ansi green_bold)activated!(ansi reset)'"
    ] | str join "\n")
}}

and the final hook looks like


env_change: {
    PWD: [
        { code: "hide toolkit" }
        (env-change pwd toolkit)
        (env-change pwd toolkit --directory)
    ]
}

-------------------------------------------

and that’s it

with all this, any time you enter a directory with either a toolkit.nu or a toolkit/ module, it will be activated and ready to use, with a little message 😌