Files, Modules, and Dependencies
Learning Objectives
- You can read files and organize a CLI program into modules.
- You know how
deno.jsonis used in a small project. - You understand how to add dependencies from
jsr:and how environment variables fit into local configuration.
File input in Deno
Command-line tools often work with local files. Deno provides built-in support for this.
To read a single text file:
const text = await Deno.readTextFile("./data/hello.txt");console.log(text);Because the program reads from the local file system, it must be run with read permission:
$ deno run --allow-read app.jsYou can also run the program without the permission flag. Then, when Deno attempts to open a file, it will ask for the permission.
Splitting a project into modules
Even a small CLI program becomes easier to maintain when responsibilities are separated.
Figure 1 shows a simple project layout that we will use in the text-processing tutorial at the end of this part.
The structure can make the program easier to explain. If a developer can say “this file reads inputs, this file performs the processing, and this file formats the output”, the project is already much easier to understand than a single long script with all three concerns mixed together.
deno.json
Deno projects often include a deno.json file for configuration and dependency declarations.
For example:
{ "imports": { "@std/path": "jsr:@std/path@1.0.8" }}With this configuration in place, the program can import the dependency using the short name:
import * as path from "@std/path";This file often becomes the place where small project-wide decisions are collected. That makes the rest of the code cleaner, because the imports and runtime settings do not need to be repeated in many places.
Files and configuration
0 / 5 points
A small Deno CLI project now has repeated commands for running and testing. Which statement about deno.json is correct in this situation?
Adding dependencies
A dependency can be added with deno add.
$ deno add @std/path@1.0.8This adds the dependency to deno.json and also updates a lock file deno.lock that keeps track of the explicit versions. The jsr prefix refers to the JavaScript Registry.
Environment variables
Programs often need small pieces of configuration that should not be hard-coded. A later LLM client, for example, may need the API URL, the model name, and the API key.
In Deno, environment variables can be read like this:
const model = Deno.env.get("LLM_MODEL") ?? "default-model";If the program reads environment variables, run it with --allow-env.
This becomes especially useful when the same program should work in different settings. For example, a developer might want to test against a different endpoint in development than in production. Environment variables make that possible without editing the source file.
Configuration and secrets
0 / 5 points
You want the same CLI code to run in multiple environments without committing secrets to the repository. Why are environment variables a good fit for API keys and model settings?
A practical structure rule
When a CLI program grows beyond a single page of code, ask whether it would be clearer if one file handled I/O, one file handled the main processing logic, and one file handled formatting or output generation.
That structure is often enough to keep a small project readable. It also makes later testing easier, because the main logic can often be tested separately from the file system and the terminal output. You can later on add new files with new responsibilities, or divide the existing files further, but this simple structure is a good starting point.
Modular task report CLI
0 / 10 points
The assignment template comes with a small CLI project that should read a JSON file of tasks and print a summary report.
Complete the project so that:
-
readTasks(filepath)reads and parses the JSON task file, -
buildTaskReport(tasks)returns an object withtotal,open, anddone, -
formatTaskReport(report)returns this exact multi-line string format:Tasks: 3Open: 2Done: 1 -
and
run(filepath)ties the modules together and returns that formatted report string.
The example task data is in data/tasks.json. When the program is run with deno run --allow-read main.js, it should print a task summary for that file.
The template also includes starter tests. Run them with read permissions:
$ deno test --allow-readYou can then try the full program manually:
$ deno run --allow-read main.js