(A good companion for numd)
dotnu augments Nushell with helpers for literate programming, dependency analysis, and script profiling.
git clone https://github.com/nushell-prophet/dotnu; cd dotnu
use dotnunupm install https://github.com/nushell-prophet/dotnu --git
# if nupm modules are not in `NU_LIB_DIRS`:
$env.NU_LIB_DIRS ++= [ ($env.NUPM_HOME | path join "modules") ]
use dotnudotnu lets you write literate Nushell: ordinary Nushell scripts that include the real command output right after each pipeline ending in | print $in.
The | print $in suffix acts as a simple print in native Nushell and as a capture marker for dotnu, so scripts remain valid and functional even when run without loading the dotnu module.
Inserts captured output back into the script at capture points
The main command of the embeds family: takes a script, rewrites every print $in line so its output is easy to parse, runs the modified script, captures what each marked line prints, and then replaces the old # => blocks in the original file with the fresh output.
Run it on a file path (e.g., dotnu embeds-update dotnu-capture.nu) or pipe a script into it (e.g., "ls | print $in" | dotnu embeds-update).
dotnu embeds-update (file) # `string -> nothing`, `string -> string`, `nothing -> string`, `nothing -> nothing`Parameters:
file?: path
Flags:
--echoโ output updates to stdout
While it is easy to write scripts in an editor, there are several convenience helper commands that facilitate populating script files from the terminal.
Embed stdin together with its command into the file
Captures only the pipeline you run it on โ useful for fine-grained examples.
dotnu embed-addFlags:
--capture-path: pathโ capture file to append to; remembered for later calls in the session--pipe-further (-p)โ output input further to the pipeline--publishedโ output the published representation into terminal--dry-run
Removes annotation lines starting with "# => " from the script
dotnu embeds-removedotnu expand-code is the inverse of embeds-update. Where embeds-update runs code and writes its output back as # => comments, expand-code runs a pipeline written inside a comment and writes that pipeline's text result back as real code lines.
A directive is a line beginning with #**; everything after the marker is a Nushell pipeline that must return text. Each line of that text becomes one generated code line, inserted right after the directive and up to a #**end marker on its own line.
Start with an empty block โ here in a module's mod.nu, just the directive and its end marker:
#** ls *.nu | where name != 'mod.nu' | get name | each { $"export use ($in) *" } | to text
#**endRunning dotnu expand-code mod.nu fills the block, re-exporting every sibling command file:
#** ls *.nu | where name != 'mod.nu' | get name | each { $"export use ($in) *" } | to text
export use config.nu *
export use greet.nu *
export use history.nu *
#**endThe directive and the #**end marker are never modified, so a re-run replaces only the lines between them โ refreshing the generated code whenever the inputs change (here, whenever you add or remove a command file). Relative paths in the pipeline resolve against the file's own directory, so ls *.nu sees the module's own files.
Like embeds-update, you can run it on a file path (dotnu expand-code file.nu) or pipe a script in and get the result back ($script | dotnu expand-code). Pass --echo to print the result instead of saving to the file.
Check .nu module files to determine which commands depend on other commands.
dotnu dependencies ...(paths)Parameters:
...paths: pathโ paths to nushell module files
Flags:
--keep-builtinsโ keep builtin commands in the result page--definitions-onlyโ output only commands' names definitions
Examples:
Analyze command dependencies in a module
dotnu dependencies ...(glob tests/assets/module-say/say/*.nu)
# => โญโโโโฌโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโฌโโโโโโโฎ
# => โ # โ caller โ filename_of_caller โ callee โ step โ
# => โโโโโผโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโผโโโโโโโค
# => โ 0 โ question โ ask.nu โ โ 0 โ
# => โ 1 โ hello โ hello.nu โ โ 0 โ
# => โ 2 โ say โ mod.nu โ hello โ 0 โ
# => โ 3 โ say โ mod.nu โ hi โ 0 โ
# => โ 4 โ say โ mod.nu โ question โ 0 โ
# => โ 5 โ hi โ mod.nu โ โ 0 โ
# => โ 6 โ test-hi โ test-hi.nu โ hi โ 0 โ
# => โฐโโโโดโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโดโโโโโโโฏFilter commands after dotnu dependencies that aren't used by any test command.
Test commands are detected by: name contains 'test' OR file matches 'test*.nu'
dotnu filter-commands-with-no-testsExamples:
Find commands not covered by tests
dependencies ...(glob tests/assets/module-say/say/*.nu) | filter-commands-with-no-tests
# => โญโโโโฌโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโฎ
# => โ # โ caller โ filename_of_caller โ
# => โโโโโผโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโค
# => โ 0 โ question โ ask.nu โ
# => โ 1 โ hello โ hello.nu โ
# => โ 2 โ say โ mod.nu โ
# => โฐโโโโดโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโฏOpen a regular .nu script. Divide it into blocks by "\n\n". Generate a new script that will print the code of each block before executing it, and print the timings of each block's execution.
dotnu set-x <file>Parameters:
file: pathโ path to.nufile
Flags:
--regex: stringโ regex to split on blocks (default: '\n+\n' - blank lines)--echoโ output script to terminal--quietโ don't print any messages
Examples:
Generate script with timing instrumentation
set-x tests/assets/set-x-demo.nu --echo | lines | first 3 | to text
# => mut $prev_ts = ( date now )
# => print ("> sleep 0.5sec" | nu-highlight)
# => sleep 0.5secExample with a simple script:
let $filename = [tests assets set-x-demo.nu] | path join
open $filename | lines | table -i false
# => โญโโโโโโโโโโโโโโโฎ
# => โ sleep 0.5sec โ
# => โ โ
# => โ sleep 0.7sec โ
# => โ โ
# => โ sleep 0.8sec โ
# => โฐโโโโโโโโโโโโโโโฏdotnu set-x $filename --echo | lines | table -i false
# => โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฎ
# => โ mut $prev_ts = ( date now ) โ
# => โ print ("> sleep 0.5sec" | nu-highlight) โ
# => โ sleep 0.5sec โ
# => โ print $'(ansi grey)((date now) - $prev_ts)(ansi reset)'; $prev_ts = (date โ
# => โ now); โ
# => โ โ
# => โ โ
# => โ print ("> sleep 0.7sec" | nu-highlight) โ
# => โ sleep 0.7sec โ
# => โ print $'(ansi grey)((date now) - $prev_ts)(ansi reset)'; $prev_ts = (date โ
# => โ now); โ
# => โ โ
# => โ โ
# => โ print ("> sleep 0.8sec" | nu-highlight) โ
# => โ sleep 0.8sec โ
# => โ print $'(ansi grey)((date now) - $prev_ts)(ansi reset)'; $prev_ts = (date โ
# => โ now); โ
# => โ โ
# => โ โ
# => โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏGenerate .numd from .nu divided into blocks by "\n\n"
Pipe a .nu script into this command to convert it into .numd format (markdown with code blocks).
dotnu generate-numd"sleep 0.5sec\n\nsleep 0.7sec" | dotnu generate-numd
# => ```nu
# => sleep 0.5sec
# => ```
# =>
# => ```nu
# => sleep 0.7sec
# => ```
# =>Extract a command with its whole dependency cascade from a module into one self-contained script. The module is imported into a clean nu -n process and command bodies are dumped via view source, so Nushell itself resolves export use chains, submodules and main renaming. Private dependencies are embedded as plain def, definitions come in dependency order, and imports of external modules (std etc.) are reproduced as use lines.
Importing a module runs its export-env blocks, so the command refuses modules containing export-env unless you pass --allow-export-env after inspecting them.
dotnu extract-module-command tests/assets/module-embed greet
# => use std/assert
# =>
# => export def greet-word [] {
# => assert true
# => 'hello'
# => }
# =>
# => def subject [] { 'world' }
# =>
# => export def greet [] { $"(greet-word) (subject)!" }Pass --vars (or a non-empty --set-vars) to turn the target into a debug scaffold instead: its parameters become let bindings you can edit, and its body is unwrapped to the top level, so sourcing the script runs the body with the variables in scope. The dependencies stay embedded as def. With --output, values you edit in the saved file are kept on re-extraction unless you pass --clear-vars.
dotnu extract-module-command tests/assets/module-embed greet-loud --vars
# => use std/assert
# =>
# => export def greet-word [] {
# => assert true
# => 'hello'
# => }
# =>
# => def subject [] { 'world' }
# =>
# => # def greet-loud [ --upper ] {
# => #dotnu-vars-start
# => let $upper = false
# => #dotnu-vars-end
# => let msg = $"(greet-word) (subject)!"
# => if $upper { $msg | str upcase } else { $msg }List all exported definitions from a module file. Finds commands from export def and export use patterns, including bare and glob re-exports (resolved by reading the referenced submodule).
dotnu list-module-exports dotnu/mod.nu | first 5
# => โญโโโโฌโโโโโโโโโโโโโโโโโโฎ
# => โ 0 โ dependencies โ
# => โ 1 โ embed-add โ
# => โ 2 โ embeds-remove โ
# => โ 3 โ embeds-update โ
# => โ 4 โ examples-update โ
# => โฐโโโโดโโโโโโโโโโโโโโโโโโฏList module's callable interface - the main and main subcommand patterns that become available when you use the module.
dotnu list-module-interface tests/assets/b/example-mod1.nu
# => โญโโโโฌโโโโโโโฎ
# => โ 0 โ main โ
# => โฐโโโโดโโโโโโโฏExtract all commands from a module as a record of {command_name: source_code}
dotnu module-commands-code-to-record <module_path>Parameters:
module_path: pathโ path to a Nushell module file
