Skip to content

Commit

Permalink
Merge pull request #15 from jollytoad/workspaces
Browse files Browse the repository at this point in the history
Convert to workspaces with publishing to JSR
  • Loading branch information
jollytoad authored May 14, 2024
2 parents 67a9924 + 27b1365 commit 07f9a7f
Show file tree
Hide file tree
Showing 194 changed files with 3,418 additions and 1,022 deletions.
23 changes: 23 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Publish
on:
push:
branches:
- jsr
- workspaces

jobs:
publish:
runs-on: ubuntu-latest

permissions:
contents: read
id-token: write

steps:
- uses: actions/checkout@v4

- name: Install Deno
uses: denoland/setup-deno@v1

- name: Publish package
run: deno publish
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
deno.lock
coverage
9 changes: 7 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
{
"deno.importMap": "./examples/import_map.json",
"deno.enable": true,
"deno.lint": true,
"deno.importMap": "./import_map_local.json",
"editor.defaultFormatter": "denoland.vscode-deno",
"editor.tabSize": 2
"editor.tabSize": 2,
"deno.codeLens.testArgs": [
"--allow-all",
"--no-check",
"--location=http://localhost:8000"
]
}
66 changes: 66 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

This changelog will need to be split between individual packages

## [0.12.0]

### Changed

- [@http/interceptor] `intercept()` error handlers applied at each lifecycle
stage, and no longer implicitly handles thrown Responses
- [@http/interceptor] `interceptResponse()` moved into own module
- [@http/interceptor] `skip()` moved into own module
- [@http/request] utility functions now take a Request as first parameter rather
than returning a function

### Added

- [@http/interceptor] `catchResponse()` to explicitly handle thrown Responses
- [@http/interceptor] tests added
- [@http/request] `despose()` added to `memoize` module
- [@http/assert] `assertHeader()` now supports `Request` and `Headers` object

## [0.10.0]

### Changed

- Split `@http/fns` into separate packages.
- [@http/generate/generate-routes-module] `httpModulePrefix` replaces `httpFns`
and `jsr` options, defaulting to `@http/`.
- [@http/interceptor/cors] `cors()` now returns an `Interceptors` objects for
direct use with `intercept()` or `init()`.
- [@http/generate] use [ts-poet](https://github.com/stephenh/ts-poet) to
generate code.

### Added

- [@http/response] `setHeaders()`, `headerEntries()` to accompany
`appendHeaders()`
- [@http/request] `memoize()`
- [@http/assert] new package of assertions for use in server tests.

### Fixed

- [@http/discovery] updated `freshPathMapper` to support latest Fresh route
syntax

## [0.7.0]

### Added

- Support for modules that export http method functions rather than a default
handler, when generating a routes module.
- `lazy` supports module transformation after loading (to allow dynamic loading
of http method modules).

### Fixed

- `generate_routes.ts` example to enable `jsr` imports.
34 changes: 10 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ one.
## The bullet points

- A library of composable functions rather than a monolithic router class
- Based on web standard [Request] => [Response] functions
- Based on web standard [Request] => [Response] functions, aka
[Fetch handlers](https://blog.val.town/blog/the-api-we-forgot-to-name/)
- Works with [`Deno.serve`][deno_serve]
- Routing based on various criteria
- URLPattern
Expand All @@ -34,9 +35,9 @@ one.
Let's start with a really simple example, a router for `GET /hello`...

```ts
import { handle } from "https://deno.land/x/http_fns/lib/handle.ts";
import { byPattern } from "https://deno.land/x/http_fns/lib/by_pattern.ts";
import { byMethod } from "https://deno.land/x/http_fns/lib/by_method.ts";
import { handle } from "@http/route/handle";
import { byPattern } from "@http/route/by_pattern";
import { byMethod } from "@http/route/by_method";

Deno.serve(handle([
byPattern(
Expand Down Expand Up @@ -173,34 +174,19 @@ of unused features. You'll find no `mod.ts` or `deps.ts` around here.

## Examples

There are many [examples](./examples) that can be executed directly, and many
tests for these examples.
There are many [examples](./packages/examples) that can be executed directly,
and many tests for these examples.

You can run them after cloning this repo, for example:

```sh
deno run -A --import-map=examples/import_map.json examples/logging.ts
deno task example packages/examples/logging.ts
```

or (using a task defined in the deno.json file)
or directly from jsr:

```sh
deno task example examples/logging.ts
```

(NOTE: The above will map the imports to use the local http_fns modules rather
than fetching from deno.land)

or directly from deno.land:

```sh
deno run -A https://deno.land/x/http_fns/examples/logging.ts
```

or directly from GitHub:

```sh
deno run -A https://raw.githubusercontent.com/jollytoad/deno_http_fns/main/examples/logging.ts
deno run -A jsr:@http/examples/logging
```

Many of the examples have accompanying tests, which I hope to improve coverage
Expand Down
2 changes: 1 addition & 1 deletion _experimental/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export type ApiProvider = (opts?: ApiProviderOptions) => ApiDefinition;
* Symbol for the property of an ApiProvider on a handler function
*/
export const Symbol_apiProvider = Symbol.for(
"https://deno.land/x/http_fns/ApiProvider",
"jsr:@http/fns/ApiProvider",
);

export type WithApiProvider = {
Expand Down
70 changes: 70 additions & 0 deletions _tools/_utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { relative } from "@std/path/relative";
import { resolve } from "@std/path/resolve";

export const rootPath = import.meta.dirname
? resolve(import.meta.dirname, "..")
: undefined;

export async function readJson(path: string) {
try {
return JSON.parse(await Deno.readTextFile(path));
} catch (e) {
if (e instanceof Deno.errors.NotFound) {
return undefined;
} else {
throw e;
}
}
}

export async function writeJson(path: string, content: unknown) {
const newContent = JSON.stringify(content, null, 2) + "\n";
const oldContent = JSON.stringify(await readJson(path), null, 2) + "\n";

if (newContent !== oldContent) {
await Deno.writeTextFile(path, newContent);
console.log(`Updated: %c${relative(rootPath!, path)}`, "font-weight: bold");
return true;
}

return false;
}

export interface DenoConfig {
name?: string;
version?: string;
imports?: Record<string, string>;
exports?: Record<string, string>;
workspaces?: string[];
}

export async function readDenoConfig(
parentPath = ".",
): Promise<DenoConfig | undefined> {
if (rootPath) {
const denoJsonPath = resolve(rootPath, parentPath, "deno.json");

return await readJson(denoJsonPath) as DenoConfig;
}

return undefined;
}

export async function writeDenoConfig(
content: DenoConfig,
parentPath = ".",
): Promise<boolean> {
if (rootPath) {
const denoJsonPath = resolve(rootPath, parentPath, "deno.json");

return await writeJson(denoJsonPath, content);
}

return false;
}

export function sortByKey<R extends Record<string, unknown>>(obj: R): R {
return Object.fromEntries(
Object.entries(obj).toSorted(([a], [b]) => a.localeCompare(b)),
) as R;
}
20 changes: 20 additions & 0 deletions _tools/fix-imports.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { walk } from "@std/fs/walk";

export async function fixStdLibImports(root: string) {
for await (const entry of walk(root, { exts: [".ts"] })) {
const content = await Deno.readTextFile(entry.path);

const replaced = content.replaceAll(/"@http\/([^"]+)"/g, (str) => {
return str.replaceAll("_", "-");
});

if (replaced !== content) {
console.log(entry.path);
await Deno.writeTextFile(entry.path, replaced);
}
}
}

if (import.meta.main) {
await fixStdLibImports(Deno.cwd());
}
54 changes: 54 additions & 0 deletions _tools/local-import-map.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#!/usr/bin/env -S deno run --allow-read=. --allow-write=.

import { resolve } from "@std/path/resolve";
import { join } from "@std/path/posix/join";
import { readDenoConfig, readJson, rootPath, writeJson } from "./_utils.ts";
import { sortByKey } from "./_utils.ts";

export async function buildLocalImportMap() {
if (rootPath) {
const importMapPath = resolve(rootPath, "import_map_local.json");
const rootDenoJson = await readDenoConfig();

if (!rootDenoJson) {
throw new Error(`deno.json not found!`);
}

const importsIn = { ...rootDenoJson.imports };
const imports: Record<string, string> = {};

for (const workspacePath of rootDenoJson.workspaces as string[]) {
const pkgDenoJsonPath = resolve(rootPath, workspacePath, "deno.json");
const pkgDenoJson = await readJson(pkgDenoJsonPath);

if (importsIn[pkgDenoJson.name]) {
delete importsIn[pkgDenoJson.name];

for (
const [alias, target] of Object.entries(
pkgDenoJson.exports as Record<string, string>,
)
) {
const fullAlias = join(pkgDenoJson.name, alias);
const fullTarget = "./" + join(workspacePath, target);
imports[fullAlias] = fullTarget;
}
}
}

Object.assign(imports, importsIn);

for (const [alias, target] of Object.entries(importsIn)) {
const m = /^(npm|jsr):([^/].+[^/])$/.exec(target);
if (m) {
imports[`${alias}/`] = `${m[1]}:/${m[2]}/`;
}
}

await writeJson(importMapPath, { imports: sortByKey(imports) });
}
}

if (import.meta.main) {
await buildLocalImportMap();
}
Loading

0 comments on commit 07f9a7f

Please sign in to comment.