Deno 2.1.7, Project Idioms
I've noticed a collection of file and code idioms I've been used in my recent Deno+TypeScript projects at work. I've captured them here as a future reference.
Project files
My project generally have the following files, these are derived from the CodeMeta file using CMTools.
- codemeta.json
- Primary source of project metadata, used to generate various files
- CITATION.cff
- used by GitHub for citations. version, dateModified, datePublished and releaseNotes
- about.md
- A project about page. This is generated based on the codemeta.json file.
- README.md, README
- A general read me describing the project and pointing to INSTALL.md, user_manual.md as appropriate
- INSTALL.md
- These are boiler plate description of how to install and compile the software
- user_manual.md
- This is an index document, a table of contents. It points to other document including Markdown versions of the man page(s).
For TypeScript projects I also include a following
- version.ts
- This hold project version information used in the TypeScript co-debase. It is generated from the codemeta.json via CMTools.
- helptext.ts
- This is where I place a function,
fmtHelp(), for rendering response to the "help" command line option.
I'm currently ambivalent about "main.ts" file which is created by deno init. My ambivalent is that most of my projects wind up producing more than one program from a shared code base. a single "main.ts" doesn't really fit that situation.
The command line tool will have a TypeScript with it's name. Inside this file I'll have a main function and use the Deno idiom if (import.meta.main) main(); to invoke it. I don't generally put the command line TypeScript in my "mod.ts" file since it's not going to work in a browser or be useful outside my specific project.
- mod.ts
- I usually re-export modules here that maybe useful outside my project (or in the web browser).
- deps.ts
- I use this if there are allot of files consistently being imported across the project, otherwise I skip it.
What I put in Main
I use the main function to define command line options, handle parameters such as data input, output and errors. It usually invokes a primary function modeled in the rest of the project code.
Here is an example Main for a simple "cat" like program.
import { parseArgs } from "jsr:@std/cli";
import { licenseText, releaseDate, releaseHash, version } from "./version.ts";
import { fmtHelp, helpText } from "./helptext.ts";
const appName = "mycat";
async function main() {
const app = parseArgs(Deno.args, {
alias: {
help: "h",
license: "l",
version: "v",
},
default: {
help: false,
version: false,
license: false,
},
});
const args = app._;
if (app.help) {
console.log(fmtHelp(helpText, appName, version, releaseDate, releaseHash));
Deno.exit(0);
}
if (app.license) {
console.log(licenseText);
Deno.exit(0);
}
if (app.version) {
console.log(`${appName} ${version} ${releaseHash}`);
Deno.exit(0);
}
let input: Deno.FsFile | any = Deno.stdin;
// handle case of many file names
if (args.length > 1) {
for (const arg of args) {
input = await Deno.open(`${arg}`);
for await (const chunk of input.readable) {
const decoder = new TextDecoder();
console.log(decoder.decode(chunk));
}
}
Deno.exit(0);
}
if (args.length > 0) {
input = await Deno.open(Deno.args[0]);
}
for await (const chunk of input.readable) {
const decoder = new TextDecoder();
console.log(decoder.decode(chunk));
}
}
if (import.meta.main) main();
helptext.ts
The following is an example of the helptext.ts file for the demo mycat.ts.
export function fmtHelp(
txt: string,
appName: string,
version: string,
releaseDate: string,
releaseHash: string,
): string {
return txt.replaceAll("{app_name}", appName).replaceAll("{version}", version)
.replaceAll("{release_date}", releaseDate).replaceAll(
"{release_hash}",
releaseHash,
);
}
export const helpText =
`%{app_name}(1) user manual | version {version} {release_hash}
% R. S. Doiel
% {release_date}
# NAME
{app_name}
# SYNOPSIS
{app_name} FILE [FILE ...] [OPTIONS]
# DESCRIPTION
{app_name} implements a "cat" like program.
# OPTIONS
Options come as the last parameter(s) on the command line.
-h, --help
: display help
-v, --version
: display version
-l, --license
: display license
# EXAMPLES
~~~shell
{app_name} README.md
{app_name} README.md INSTALL.md
~~~
`;
Generating version.ts
The version.ts is generated form two files, [codemeta.json] and [LICENSE] using the CMTools, cmt command.
cmt codemeta.json veresion.ts