@@ -4,3 +4,323 @@ url: /examples/command_line_utility/
4
4
videoUrl : https://www.youtube.com/watch?v=TUxj2TS5pNo&list=PLvvLnBDNuTEov9EBIp3MMfHlBxaKGRWTe&index=14
5
5
layout : video.tsx
6
6
---
7
+
8
+ ## Video description
9
+
10
+ Learn to build a command line tool using Deno's standard library. You'll explore
11
+ how to parse arguments, handle flags, and provide helpful messages using utility
12
+ functions. Follow along as we build a ski resort information app, handle errors
13
+ gracefully, and compile the script into an executable for multiple platforms,
14
+ including Windows, MacOS, and Linux. By the end of this video, you'll understand
15
+ how to take full advantage of Deno's features to develop and distribute your own
16
+ CLI tools.
17
+
18
+ ## Transcript and code
19
+
20
+ ### An introduction to Deno's Standard Library
21
+
22
+ If you want to create a command line tool you can do so with
23
+ [ Deno's standard Library] ( https://docs.deno.com/runtime/fundamentals/standard_library/ ) .
24
+ It contains dozens of stable libraries with helpful utility functions that can
25
+ cover a lot of the basics when working with JavaScript in the web. The standard
26
+ Library also works in multiple runtimes and environments like Node.js and the
27
+ browser.
28
+
29
+ ### Setting up a command line tool
30
+
31
+ We're going to create a commandline tool, and then we're going to compile it so
32
+ it can be used on a number of different platforms as an executable.
33
+
34
+ Create a new file called ` main.ts ` and parse these arguments (remember we can
35
+ always grab them from ` Deno.args ` ), and then we'll console log them:
36
+
37
+ ``` typescript title="main.ts"
38
+ const location = Deno .args [0 ];
39
+
40
+ console .log (` Welcome to ${location } ` );
41
+ ```
42
+
43
+ Now if I run ` deno main.ts ` and then I provide the name of a ski resort like
44
+ Aspen that's going to plug that into the string, eg:
45
+
46
+ ``` sh
47
+ deno main.ts Aspen
48
+ # # Welcome to Aspen
49
+ ```
50
+
51
+ ### Installing and Using Standard Libraries
52
+
53
+ Now lets install one of those standard libraries. In the terminal run:
54
+
55
+ ``` sh
56
+ deno add jsr:@std/cli
57
+ ```
58
+
59
+ This is going to install the [ cli library] ( https://jsr.io/@std/cli ) , from the
60
+ Deno standard library, into our project so we could make use of some of their
61
+ helpful functions.
62
+
63
+ The Helpful function that we'll use here is called ` parseArgs ` . We can import
64
+ that with:
65
+
66
+ ``` typescript
67
+ import { parseArgs } from " jsr:@std/cli/parse-args" ;
68
+ ```
69
+
70
+ Then we can update our code to use this function, passing the argument and
71
+ removing the zero. Our ` main.ts ` file now looks like this:
72
+
73
+ ``` typescript title="main.ts"
74
+ import { parseArgs } from " jsr:@std/cli/parse-args" ;
75
+
76
+ const args = parseArgs (Deno .args );
77
+
78
+ console .log (args );
79
+ ```
80
+
81
+ Let's go ahead and try this out, in your terminal run:
82
+
83
+ ``` sh
84
+ deno main.ts -h Hello
85
+ ```
86
+
87
+ We can see that ` Hello ` has been added to our args object. All right, so that's
88
+ working as expected.
89
+
90
+ ### Building the Ski Resort Information App
91
+
92
+ Now our app is going to be a ski resort information app, so we want to populate
93
+ our app with a little bit of data to start. We're going to create a value called
94
+ ` resorts ` . This is an object with a few different keys so we'll say ` elevation ` ,
95
+ ` snow ` and ` expectedSnowfall ` . Then let's just copy and paste these so that we
96
+ can move a little more quickly we'll set ` Aspen ` to ` 7945 ` ` snow ` to
97
+ ` packed powder ` , ` expectedSnowfall ` to ` 15 ` . Then let's add one more of these
98
+ we'll set ` Vail ` to ` 8120 ` and then we'll say ` expectedSnowfall ` is ` 25 ` .
99
+
100
+ ``` typescript title="main.ts"
101
+ const resorts = {
102
+ Whistler: {
103
+ elevation: 2214 ,
104
+ snow: " Powder" ,
105
+ expectedSnowfall: " 20" ,
106
+ },
107
+
108
+ Aspen: {
109
+ elevation: 7945 ,
110
+ snow: " packed powder" ,
111
+ expectedSnowfall: 15 ,
112
+ },
113
+ Vail: {
114
+ elevation: 8120 ,
115
+ snow: " packed powder" ,
116
+ expectedSnowfall: 25 ,
117
+ },
118
+ };
119
+ ```
120
+
121
+ We have a few different resorts here. Ultimately we want to be able to run our
122
+ app with a command line argument that's going to provide the resort name and
123
+ then have that CLI tool return the information about that resort.
124
+
125
+ ### Handling Command Line Arguments
126
+
127
+ So let's go ahead and pass another object to parse args, here we're going to
128
+ define an alias - so we're going to say "if I pass the ` r ` flag we want to have
129
+ it assume it means ` resort ` . Then let's also use the default here, we'll set the
130
+ ` default ` ` resort ` to ` Whistler ` :
131
+
132
+ ``` typescript title="main.ts"
133
+ const args = parseArgs (Deno .args , {
134
+ alias: {
135
+ resort: " r" ,
136
+ },
137
+ default: {
138
+ resort: " Whistler" ,
139
+ },
140
+ });
141
+ ```
142
+
143
+ From here we can set up a const called ` resortName ` and set it to ` args.resort ` .
144
+ Then get the resort, with ` resorts[resortName] ` (we'll fix that type error in a
145
+ second), and update the console log:
146
+
147
+ ``` typescript title="main.ts"
148
+ const resortName = args .resort ;
149
+ const resort = resorts [resortName ];
150
+
151
+ console .log (
152
+ ` Resort: ${resortName } Elevation: ${resort .elevation } feet Snow: ${resort .snow } Expected Snowfall: ${resort .expectedSnowfall } ` ,
153
+ );
154
+ ```
155
+
156
+ To test this out we can use:
157
+
158
+ ``` sh
159
+ deno main.ts -r Aspen
160
+ ```
161
+
162
+ Which will give us a printout of all of Aspen's details.
163
+
164
+ We can also run this without any arguments which should give the details for
165
+ Whistler, because that was set as default:
166
+
167
+ ``` sh
168
+ deno main.ts
169
+ ```
170
+
171
+ Same goes for our full name, so we could say:
172
+
173
+ ``` sh
174
+ deno main.ts --resort Veil
175
+ ```
176
+
177
+ And that should give us those details as well.
178
+
179
+ ### Improving Error Handling
180
+
181
+ Now if I tried to run this with a resort that's not there, let's say ` Bachelor ` ;
182
+ there's an error so that's kind of an ugly one. It's hitting this moment where
183
+ it's trying to parse that out and it can't find it. So we could make this a
184
+ little nicer by saying if there's no ` resort ` in our data set that matches the
185
+ input, let's run a console error saying
186
+ ` resort name not found, try Whistler Aspen or Veil ` and then we'll hop out of
187
+ that process with a ` Deno.exit ` :
188
+
189
+ ``` typescript title="main.ts"
190
+ if (! resort ) {
191
+ console .error (
192
+ ` Resort ${resortName } name not found. Try Whistler, Aspen, or Veil ` ,
193
+ );
194
+ Deno .exit (1 );
195
+ }
196
+ ```
197
+
198
+ ### Fixing the types
199
+
200
+ Okay so this here isn't looking so good we can look at the problems here in
201
+ typescript - it's telling us that this implicitly has an ` any ` type, you can
202
+ look up more about this error but I'll show you how to fix this one. Update the
203
+ type of ` resortName ` to be a key of ` resorts ` :
204
+
205
+ ``` typescript title="main.ts"
206
+ const resortName = args .resort as keyof typeof resorts ;
207
+ ```
208
+
209
+ What this has done is extract the value of ` args.resort ` and it's going to
210
+ assert that there is a valid key inside of the data.
211
+
212
+ ### Adding Help and Color Output
213
+
214
+ Let's take this one more step, we're going to say if ` args.help ` , we will
215
+ console log and then we're going to give our users a little message to say "hey
216
+ this is actually how you use this" if they do happen to ask for help at any
217
+ moment, and we'll update the alias here to say ` help ` is ` H ` , finally we'll make
218
+ sure to call ` Deno.exit ` so that we jump out of the process as soon as we're
219
+ done with that:
220
+
221
+ ``` typescript title="main.ts"
222
+ const args = parseArgs (Deno .args , {
223
+ alias: {
224
+ resort: " r" ,
225
+ help: " h" ,
226
+ },
227
+ default: {
228
+ resort: " Whistler" ,
229
+ },
230
+ });
231
+
232
+ ...
233
+
234
+ if (args .help ) {
235
+ console .log (`
236
+ usage: ski-cli --resort <resort name>
237
+ -h, --help Show Help
238
+ -r, --resort Name of the ski resort (default: Whistler)
239
+ ` );
240
+ Deno .exit ();
241
+ }
242
+ ```
243
+
244
+ You can test your help setup by running the following:
245
+
246
+ ``` sh
247
+ deno main.ts -h
248
+ ```
249
+
250
+ Next let's log our results here in color. Deno has support for CSS using the
251
+ ` %C ` syntax.
252
+
253
+ This will take the text and apply the style that we pass in as the second
254
+ argument to the ` console.log() ` . Here we could set ` color:blue ` as the second
255
+ argument, eg:
256
+
257
+ ```` typescript title="main.ts"
258
+ console .log (`
259
+ %c
260
+ Resort: ${resortName }
261
+ Elevation: ${resort .elevation } feet
262
+ Snow: ${resort .snow }
263
+ Expected Snowfall: ${resort .expectedSnowfall }
264
+ ` , " color:blue"
265
+ );
266
+
267
+ Then run the program again :
268
+
269
+ ` ` ` sh
270
+ deno main.ts -r Veil
271
+ ` ` ` `
272
+
273
+ You should see everything logged in a blue color. How cool is that?!
274
+
275
+ ### Compiling the Tool for Different Platforms
276
+
277
+ I want other people to be able to enjoy the app too. Compiling this tool into an
278
+ executable is pretty easy with Deno. As you might imagine, the command for
279
+ running this is ` deno compile ` and then the name of our script. This is going to
280
+ compile the code to the project as an executable:
281
+
282
+ ` ` ` sh
283
+ deno compile main .ts
284
+ ```
285
+
286
+ You should see the executable in your project folder called MyDenoProject . Now
287
+ you can run this as an executable with ` ./ ` , eg :
288
+
289
+ ` ` ` sh
290
+ ./MyDenoProject --resort Aspen
291
+ ` ` `
292
+
293
+ So this is really great for me , but what happens if I want to share this to
294
+ other platforms ? All you would need to do is run ` deno compile ` again , this time
295
+ passing in a ` --target ` flag for where you want to compile to .
296
+
297
+ Let ' s say we wanted to compile it for Windows we' d use :
298
+
299
+ ` ` ` sh
300
+ deno compile --target x86_64-pc-windows-msvc --output ski-cli-windows main.ts
301
+ ` ` `
302
+
303
+ or for a Mac :
304
+
305
+ ` ` ` sh
306
+ deno compile --target x86_64-apple-darwin --output ski-cli-macos main.ts
307
+ ` ` `
308
+
309
+ or for Linux :
310
+
311
+ ` ` ` sh
312
+ deno compile --target x86_64-unknown-linux-gnu --output ski-cli-linux main.ts
313
+ ` ` `
314
+
315
+ You can see all of the
316
+ [options for compiling your apps ](/ runtime / reference / cli / compile / ) in the Deno
317
+ documentation . There are a lot of different flags that you can use for your own
318
+ specific use cases .
319
+
320
+ To recap we always have access to the Deno Standard Library that we can take
321
+ advantage of with all these different helpful functions . If we wanted to create
322
+ a command line utility , like we ' ve done here, we always have access to the
323
+ [` Deno ` global namespace ](/ api / deno / ~ / Deno ) for these arguments . We can parse
324
+ the arguments using the parse args function from the standard Library CLI
325
+ package and we can run a compile for all platforms so that our app can be
326
+ consumed anywhere.
0 commit comments