Como criar um módulo Deno
O Deno é um runtime JavaScript e TypeScript desenvolvido pelo mesmo criador do Node.js, Rayan Dahl. Pode ser uma opção ao próprio Node.js, com um maior nível de simplicidade, segurança e velocidade.
Uma dessas "vantagens" em simplicidade é a eliminação do grade, literalmente, node_modules, pasta que armazena pacotes externos ao seu projeto e do package.json, arquivo responsável por algumas características do próprio pacote, como nome, autor, versão, dependências, etc.
Ambas entidades estão presente em todo projeto Node.js. Mas agora com o Deno, que os não contém, como criar um módulo para uso externo? É mais simples do que eu poderia imaginar.
Shut up, and show me the code!
Antes de começar, esse é o ambiente de desenvolvimento Deno que estou utilizando no momento.
ruan@Ubuntu:~/Documentos/Dev/Blog/Deno$ deno --version
deno 1.5.4 (bc79d55, release, x86_64-unknown-linux-gnu)
v8 8.8.278.2
typescript 4.0.5
Módulo
O mínimo para um módulo Deno é simples, composto basicamente de dois arquivos. deps.ts e mod.ts. Para exemplificar criei uma pasta com o nome module e os dois arquivos citados. Ambos terão suas funções definidas a seguir:
module/
├── deps.ts
└── mod.ts
Tentando demostrar o uso de ambos no mundo real, utilizo o código do módulo Oak, framework inspirado no Koa.js, como exemplo nesse Blog.
deps.ts
Como o Deno não possui um gerenciador de pacotes como o npm, deps.ts centraliza as dependências do projeto, estas então são exportadas através de URLs externas para serem utilizadas no módulo. Parece redundante ser algo como "exportação de pacotes externos", mas acredito que faz um pouco de sentido pela simplicidade. O manual do Deno recomenda que todas as importações dos módulos externos sejam feitas a partir do deps.ts ou de variações desse, dependendo do ambiente*.
Nesse exemplo exporto a classe Application e Router do módulo Oak.
// deps.ts
export {Application, Router} from 'https://deno.land/x/oak@v6.3.2/mod.ts';
Nessa exportação defino uma versão para Oak, 6.3.2. Porém, a exportação pode não ter uma versão específica.
// deps.ts
export {Application, Router} from 'https://deno.land/x/oak/mod.ts';
Desse modo o Deno exporta a versão mais recente e avisa na saída padrão o número da versão utilizada.
Arquivo deps.ts do Oak.
// Copyright 2018-2020 the oak authors. All rights reserved. MIT license.
// This file contains the external dependencies that oak depends upon
// `std` dependencies
export { copyBytes, equal } from "https://deno.land/std@0.77.0/bytes/mod.ts";
export { Sha1 } from "https://deno.land/std@0.77.0/hash/sha1.ts";
export { HmacSha256 } from "https://deno.land/std@0.77.0/hash/sha256.ts";
export { serve, serveTLS } from "https://deno.land/std@0.77.0/http/server.ts";
export {
Status,
STATUS_TEXT,
} from "https://deno.land/std@0.77.0/http/http_status.ts";
export { BufReader, BufWriter } from "https://deno.land/std@0.77.0/io/bufio.ts";
export {
basename,
extname,
isAbsolute,
join,
normalize,
parse,
sep,
} from "https://deno.land/std@0.77.0/path/mod.ts";
export { assert } from "https://deno.land/std@0.77.0/testing/asserts.ts";
export {
acceptable,
acceptWebSocket,
} from "https://deno.land/std@0.77.0/ws/mod.ts";
export type { WebSocket } from "https://deno.land/std@0.77.0/ws/mod.ts";
// 3rd party dependencies
export {
contentType,
extension,
lookup,
} from "https://deno.land/x/media_types@v2.5.2/mod.ts";
export {
compile,
parse as pathParse,
pathToRegexp,
} from "https://deno.land/x/path_to_regexp@v6.2.0/index.ts";
export type {
Key,
ParseOptions,
TokensToRegexpOptions,
} from "https://deno.land/x/path_to_regexp@v6.2.0/index.ts";
mod.ts
Esse é o ponto de entrada público do módulo. Normalmente contém a exportação de arquivos do próprio módulo para acesso de terceiros.
No módulo de exemplo apenas exporto uma função createServer do arquivo server.ts que está na raiz do projeto.
//mod.ts
export {createServer} from './server.ts';
O conteúdo do serve.ts é simples: É a definição de uma nova Application e uma Router, adicção de duas rotas, o registro da Router como middleware no Application e eu inicio um servidor na porta 3000.
//server.ts
import {Application, Router} from "./deps.ts";
export async function createServer() {
const app = new Application();
const router = new Router();
router
.get("/", (context) => {
context.response.body = "Aqui é a home";
})
.get("/about", (context) => {
context.response.body = "Aqui é a a about";
});
app.use(router.routes());
app.addEventListener('listen', () => {
console.log("Servidor iniciado");
});
await app.listen({ port: 3000 });
}
Arquivo mod.ts do Oak:
// Copyright 2018-2020 the oak authors. All rights reserved. MIT license.
export { Application } from "./application.ts";
export type {
ApplicationOptions,
ListenOptions,
ListenOptionsBase,
ListenOptionsTls,
State,
} from "./application.ts";
export type {
Body,
BodyForm,
BodyFormData,
BodyJson,
BodyOptions,
BodyRaw,
BodyReader,
BodyText,
BodyType,
BodyUndefined,
} from "./body.ts";
export { Context } from "./context.ts";
export type { ContextSendOptions } from "./context.ts";
export * as helpers from "./helpers.ts";
export { Cookies } from "./cookies.ts";
export type { CookiesGetOptions, CookiesSetDeleteOptions } from "./cookies.ts";
export { HttpError, httpErrors, isHttpError } from "./httpError.ts";
export { compose as composeMiddleware } from "./middleware.ts";
export type { Middleware } from "./middleware.ts";
export { FormDataReader } from "./multipart.ts";
export type {
FormDataBody,
FormDataFile,
FormDataReadOptions,
} from "./multipart.ts";
export { Request } from "./request.ts";
export { REDIRECT_BACK, Response } from "./response.ts";
export { Router } from "./router.ts";
export type {
Route,
RouteParams,
RouterAllowedMethodsOptions,
RouterContext,
RouterMiddleware,
RouterOptions,
RouterParamMiddleware,
} from "./router.ts";
export { send } from "./send.ts";
export type { SendOptions } from "./send.ts";
export { ServerSentEvent, ServerSentEventTarget } from "./server_sent_event.ts";
export type { ServerSentEventInit } from "./server_sent_event.ts";
export type {
ErrorStatus,
HTTPMethods,
RedirectStatus,
ServerRequest,
ServerResponse,
} from "./types.d.ts";
export { isErrorStatus, isRedirectStatus } from "./util.ts";
// Re-exported from `net`
export { Status, STATUS_TEXT } from "./deps.ts";
O deps.ts e mod.ts são apenas uma parte do desenvolvimento do módulo, a parte convencional dele. Além disso, há escolhas de domain e arquitetura relativas a cada projeto, testes, e etc que não caberia nesse simples Blog. Claro que para informações mais precisas ou aprofundadas, como a questão de diferentes arquivos deps.ts e uso de JavaScript ao invés de TypeScript, a Documentação supre muito bem, apesar de estar em inglês.
Sintam-se livres para comentar e criticar positivamente. Agradeço pela leitura.