Skip to content

Commit 68620e2

Browse files
132iklNotTheDr01ds
andauthored
Highlight the differences between scripts and modules (#1567)
* Highlight the differences between scripts and modules * Change bespoke note to a tip * Address review notes Co-authored-by: Douglas <[email protected]> --------- Co-authored-by: Douglas <[email protected]>
1 parent 3b179ab commit 68620e2

File tree

2 files changed

+105
-59
lines changed

2 files changed

+105
-59
lines changed

book/modules.md

Lines changed: 66 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,12 @@ then
7878

7979
The result should be similar as in the previous section.
8080

81-
> **Note**
82-
> that the `use greetings.nu hello` call here first implicitly creates the `greetings` module,
83-
> then takes `hello` from it. You could also write it as `module greetings.nu`, `use greetings hello`.
84-
> Using `module` can be useful if you're not interested in any definitions from the module but want to,
85-
> e.g., re-export the module (`export module greetings.nu`).
81+
::: tip Note
82+
Note that the `use greetings.nu hello` call here first implicitly creates the `greetings` module,
83+
then takes `hello` from it. You could also write it as `module greetings.nu`, `use greetings hello`.
84+
Using `module` can be useful if you're not interested in any definitions from the module but want to,
85+
e.g., re-export the module (`export module greetings.nu`).
86+
:::
8687

8788
## Modules from Directories
8889

@@ -112,7 +113,7 @@ then
112113

113114
The name of the module follows the same rule as module created from a file: Stem of the directory name, i.e., the directory name, is used as the module name. Again, you could do this as a two-step action using `module` and `use` separately, as explained in the previous section.
114115

115-
::: tip
116+
::: tip Note
116117
You can define `main` command inside `mod.nu` to create a command named after the module directory.
117118
:::
118119

@@ -186,6 +187,8 @@ The `main` is exported only when
186187

187188
Importing definitions selectively (`use greetings.nu hello` or `use greetings.nu [hello hi]`) does not define the `greetings` command from `main`. You can, however, selectively import `main` using `use greetings main` (or `[main]`) which defines _only_ the `greetings` command without pulling in `hello` or `hi`.
188189

190+
Additionally, `main` has special behavior if used in a script file, regardless of whether it is exported or not. See the [section on scripts](scripts.html#parameterizing-scripts) for more details.
191+
189192
Apart from commands (`def`, `def --env`), known externals (`extern`) can also be named `main`.
190193

191194
## Submodules and Subcommands
@@ -197,8 +200,9 @@ Submodules are modules inside modules. They are automatically created when you c
197200

198201
The difference is that `export module some-module` _only_ adds the module as a submodule, while `export use some-module` _also_ re-exports the submodule's definitions. Since definitions of submodules are available when importing from a module, `export use some-module` is typically redundant, unless you want to re-export its definitions without the namespace prefix.
199202

200-
> **Note**
201-
> `module` without `export` defines only a local module, it does not export a submodule.
203+
::: tip Note
204+
`module` without `export` defines only a local module, it does not export a submodule.
205+
:::
202206

203207
Let's illustrate this with an example. Assume three files:
204208

@@ -425,58 +429,68 @@ A common pattern in traditional shells is dumping and auto-sourcing files from a
425429
Here we'll create a simple completion module with a submodule dedicated to some Git completions:
426430

427431
1. Create the completion directory
428-
`mkdir ($nu.default-config-dir | path join completions)`
432+
433+
`mkdir ($nu.default-config-dir | path join completions)`
434+
429435
2. Create an empty `mod.nu` for it
430-
`touch ($nu.default-config-dir | path join completions mod.nu)`
436+
437+
`touch ($nu.default-config-dir | path join completions mod.nu)`
438+
431439
3. Put the following snippet in `git.nu` under the `completions` directory
432-
```nu
433-
export extern main [
434-
--version(-v)
435-
-C: string
436-
# ... etc.
437-
]
438-
439-
export extern add [
440-
--verbose(-v)
441-
--dry-run(-n)
442-
# ... etc.
443-
]
444-
445-
export extern checkout [
446-
branch: string@complete-git-branch
447-
]
448-
449-
def complete-git-branch [] {
450-
# ... code to list git branches
451-
}
452-
```
440+
441+
```nu
442+
export extern main [
443+
--version(-v)
444+
-C: string
445+
# ... etc.
446+
]
447+
448+
export extern add [
449+
--verbose(-v)
450+
--dry-run(-n)
451+
# ... etc.
452+
]
453+
454+
export extern checkout [
455+
branch: string@complete-git-branch
456+
]
457+
458+
def complete-git-branch [] {
459+
# ... code to list git branches
460+
}
461+
```
462+
453463
4. Add `export module git.nu` to `mod.nu`
454464
5. Add the parent of the `completions` directory to your NU_LIB_DIRS inside `env.nu`
455-
```nu
456-
$env.NU_LIB_DIRS = [
457-
...
458-
$nu.default-config-dir
459-
]
460-
```
461-
6. import the completions to Nushell in your `config.nu`
462-
`use completions *`
463-
Now you've set up a directory where you can put your completion files and you should have some Git completions the next time you start Nushell
464465

465-
> **Note**
466-
> This will use the file name (in our example `git` from `git.nu`) as the module name. This means some completions might not work if the definition has the base command in its name.
467-
> For example, if you defined our known externals in our `git.nu` as `export extern 'git push' []`, etc. and followed the rest of the steps, you would get subcommands like `git git push`, etc.
468-
> You would need to call `use completions git *` to get the desired subcommands. For this reason, using `main` as outlined in the step above is the preferred way to define subcommands.
466+
```nu
467+
$env.NU_LIB_DIRS = [
468+
...
469+
$nu.default-config-dir
470+
]
471+
```
472+
473+
6. Import the completions to Nushell in your `config.nu`:
474+
475+
`use completions *`
476+
477+
Now you've set up a directory where you can put your completion files, and you should have some Git completions the next time you start Nushell.
478+
479+
::: tip Note
480+
This will use the file name (in our example `git` from `git.nu`) as the module name. This means some completions might not work if the definition has the base command in its name.
481+
For example, if you defined our known externals in our `git.nu` as `export extern 'git push' []`, etc. and followed the rest of the steps, you would get subcommands like `git git push`, etc.
482+
You would need to call `use completions git *` to get the desired subcommands. For this reason, using `main` as outlined in the step above is the preferred way to define subcommands.
483+
:::
469484

470485
### Setting environment + aliases (conda style)
471486

472487
`def --env` commands, `export-env` block and aliases can be used to dynamically manipulate "virtual environments" (a concept well known from Python).
473488

474-
We use it in our official virtualenv integration https://github.com/pypa/virtualenv/blob/main/src/virtualenv/activation/nushell/activate.nu
489+
We use it in our [official virtualenv integration](https://github.com/pypa/virtualenv/blob/main/src/virtualenv/activation/nushell/activate.nu). Another example is our [unofficial Conda module](https://github.com/nushell/nu_scripts/blob/main/modules/virtual_environments/conda.nu).
475490

476-
Another example could be our unofficial Conda module: https://github.com/nushell/nu_scripts/blob/f86a060c10f132407694e9ba0f536bfe3ee51efc/modules/virtual_environments/conda.nu
477-
478-
> **Warning**
479-
> Work In Progress
491+
::: warning
492+
Work in progress
493+
:::
480494

481495
## Hiding
482496

@@ -515,5 +529,6 @@ It can be one of the following:
515529

516530
- Hides all the module's exports, without the prefix
517531

518-
> **Note**
519-
> `hide` is not a supported keyword at the root of a module (unlike `def` etc.)
532+
:::tip Note
533+
`hide` is not a supported keyword at the root of a module (unlike `def` etc.)
534+
:::

book/scripts.md

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ greet "world"
2525

2626
A script file defines the definitions for custom commands as well as the main script itself, which will run after the custom commands are defined.
2727

28-
In the above, first `greet` is defined by the Nushell interpreter. This allows us to later call this definition. We could have written the above as:
28+
In the above example, first `greet` is defined by the Nushell interpreter. This allows us to later call this definition. We could have written the above as:
2929

3030
```nu
3131
greet "world"
@@ -75,9 +75,9 @@ def main [x: int] {
7575

7676
## Argument Type Interpretation
7777

78-
By default, arguments provided to a script are interpreted with the type `Type::Any`, implying that they are not constrained to a specific data type and can be dynamically interpreted as fitting any of the available data types during script execution.
78+
By default, arguments provided to a script are interpreted with the type `Type::Any`, implying that they are not constrained to a specific data type and can be dynamically interpreted as fitting any of the available data types during script execution.
7979

80-
In the previous example, `main [x: int]` denotes that the argument x should possess an integer data type. However, if arguments are not explicitly typed, they will be parsed according to their apparent data type.
80+
In the previous example, `main [x: int]` denotes that the argument x should possess an integer data type. However, if arguments are not explicitly typed, they will be parsed according to their apparent data type.
8181

8282
For example:
8383

@@ -103,9 +103,7 @@ Hello string +1
103103

104104
## Subcommands
105105

106-
A script can have multiple sub-commands like `run`, `build`, etc. which allows to execute a specific main sub-function. The important part is to expose them correctly with `def main [] {}`. See more details in the [Custom Command](custom_commands.html#sub-commands) section.
107-
108-
For example:
106+
A script can have multiple [subcommands](custom_commands.html#subcommands), like `run` or `build` for example:
109107

110108
```nu
111109
# myscript.nu
@@ -117,17 +115,48 @@ def "main build" [] {
117115
print "building"
118116
}
119117
120-
# important for the command to be exposed to the outside
121-
def main [] {}
118+
def main [] {
119+
print "hello from myscript!"
120+
}
122121
```
123122

123+
You can then execute the script's subcommands when calling it:
124+
124125
```nu
126+
> nu myscript.nu
127+
hello from myscript!
125128
> nu myscript.nu build
126129
building
127130
> nu myscript.nu run
128131
running
129132
```
130133

134+
[Unlike modules](modules.html#main), `main` does _not_ need to exported in order to be visible. In the above example, our `main` command is not `export def`, however it was still executed when running `nu myscript.nu`. If we had used myscript as a module by running `use myscript.nu`, rather than running `myscript.nu` as a script, trying to execute the `myscript` command would not work since `myscript` is not exported.
135+
136+
It is important to note that you must define a `main` command in order for subcommands of `main` to be correctly exposed. For example, if we had just defined the `run` and `build` subcommands, they wouldn't be accessible when running the script:
137+
138+
```nu
139+
# myscript.nu
140+
def "main run" [] {
141+
print "running"
142+
}
143+
144+
def "main build" [] {
145+
print "building"
146+
}
147+
```
148+
149+
```nu
150+
> nu myscript.nu build
151+
> nu myscript.nu run
152+
```
153+
154+
This is a limitation of the way scripts are currently processed. If your script only has subcommands, you can add an empty `main` to expose the subcommands, like so:
155+
156+
```nu
157+
def main [] {}
158+
```
159+
131160
## Shebangs (`#!`)
132161

133162
On Linux and macOS you can optionally use a [shebang](<https://en.wikipedia.org/wiki/Shebang_(Unix)>) to tell the OS that a file should be interpreted by Nu. For example, with the following in a file named `myscript`:
@@ -141,7 +170,9 @@ On Linux and macOS you can optionally use a [shebang](<https://en.wikipedia.org/
141170
> ./myscript
142171
Hello World!
143172
```
173+
144174
For script to have access to standard input, `nu` should be invoked with `--stdin` flag:
175+
145176
```nu
146177
#!/usr/bin/env -S nu --stdin
147178
echo $"stdin: ($in)"

0 commit comments

Comments
 (0)