@tahminator/sapling 1.5.22 → 1.5.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +102 -16
- package/dist/src/html/404.js +1 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,39 +3,47 @@
|
|
|
3
3
|
[](https://sonarcloud.io/dashboard?id=tahminator_sapling)
|
|
4
4
|
[](https://www.npmjs.com/package/@tahminator/sapling)
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
A lightweight library that brings some structure to Express.js
|
|
6
|
+
A lightweight Express.js dependency injection & route abstraction library.
|
|
8
7
|
|
|
9
8
|
## Table of Contents
|
|
10
9
|
|
|
10
|
+
<!-- toc -->
|
|
11
|
+
|
|
11
12
|
- [Why?](#why)
|
|
12
13
|
- [Examples](#examples)
|
|
13
14
|
- [Install](#install)
|
|
14
15
|
- [Quick Start](#quick-start)
|
|
15
16
|
- [Features](#features)
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
17
|
+
* [Controllers](#controllers)
|
|
18
|
+
* [HTTP Methods](#http-methods)
|
|
19
|
+
* [Responses](#responses)
|
|
20
|
+
* [Error Handling](#error-handling)
|
|
21
|
+
* [Middleware](#middleware)
|
|
22
|
+
* [Redirects](#redirects)
|
|
23
|
+
* [Dependency Injection](#dependency-injection)
|
|
24
|
+
* [Custom Serialization](#custom-serialization)
|
|
25
|
+
- [Advanced Setup](#advanced-setup)
|
|
26
|
+
* [Automatically import controllers](#automatically-import-controllers)
|
|
27
|
+
- [License](#license)
|
|
28
|
+
|
|
29
|
+
<!-- tocstop -->
|
|
30
|
+
|
|
31
|
+
<!-- if toc does not update automatically, `markdown-toc -i README.md` -->
|
|
24
32
|
|
|
25
33
|
## Why?
|
|
26
34
|
|
|
27
|
-
1. Express is
|
|
35
|
+
1. Express is a fantastic way to build server-side apps in JavaScript, but wiring can get messy very quickly. Sapling abstracts away complicated wiring of controllers & routes, allowing you to focus on business logic & write unit tests in a painless way.
|
|
28
36
|
|
|
29
|
-
2.
|
|
30
|
-
- The best reason to use Sapling is that you can
|
|
37
|
+
2. Sapling is inspired by Spring, but without losing the developer experience, speed & simplicity of Express.js / TypeScript.
|
|
38
|
+
- The best reason to use Sapling is that you can opt-in or opt-out as much as you would like; run any traditional & functional Express.js without having to hack around the library.
|
|
31
39
|
|
|
32
|
-
3.
|
|
40
|
+
3. Sapling DI & routing is designed to be very light. This may be preferable to other libraries like Nest.js that provide a much heavier abstraction. Get what would be helpful to your improve development speed, ignore anything else that may get in your way.
|
|
33
41
|
|
|
34
42
|
## Examples
|
|
35
43
|
|
|
36
|
-
Check the `/example` folder for a basic todo app with database integration.
|
|
44
|
+
Check the [`/example`](./example) folder for a basic todo app with database integration.
|
|
37
45
|
|
|
38
|
-
Sapling is also powering one of my more complex projects with
|
|
46
|
+
Sapling is also powering one of my more complex projects with 660+ users in production, which you can view at [instalock-web/apps/server](https://github.com/tahminator/instalock-web/blob/main/apps/server/src/index.ts).
|
|
39
47
|
|
|
40
48
|
## Install
|
|
41
49
|
|
|
@@ -282,6 +290,84 @@ Sapling.setDeserializeFn(superjson.parse);
|
|
|
282
290
|
|
|
283
291
|
This affects how `ResponseEntity` serializes response bodies and how request bodies are deserialized.
|
|
284
292
|
|
|
293
|
+
## Advanced Setup
|
|
294
|
+
|
|
295
|
+
### Automatically import controllers
|
|
296
|
+
|
|
297
|
+
> [!NOTE]
|
|
298
|
+
> You need ESLint (or some alternative build step that has glob-import support)
|
|
299
|
+
|
|
300
|
+
Controllers can be automatically imported via a glob-import if you ensure that all controller files are:
|
|
301
|
+
|
|
302
|
+
- `export default` (so one controller per file)
|
|
303
|
+
- all controller files are marked as `*.controller.ts`
|
|
304
|
+
|
|
305
|
+
. The steps below indicate a working example inside of my webapp, [instalock-web/apps/server](https://github.com/tahminator/instalock-web/blob/main/apps/server/src/index.ts).
|
|
306
|
+
|
|
307
|
+
1. Create a bootstrap file that will glob-import all controller files and export them
|
|
308
|
+
|
|
309
|
+
[example file](https://github.com/tahminator/instalock-web/blob/main/apps/server/src/bootstrap.ts)
|
|
310
|
+
|
|
311
|
+
```ts
|
|
312
|
+
// file will automatically import any controller files
|
|
313
|
+
// it will pull out default exports, so ensure
|
|
314
|
+
// 1. one class per file
|
|
315
|
+
// 2. `export default XyzController`
|
|
316
|
+
|
|
317
|
+
// q: wont this break ordering of controller imports?
|
|
318
|
+
// a: yes but that's ok - controllers are the last in the dependency graph.
|
|
319
|
+
// they import, but are never imported themselves.
|
|
320
|
+
|
|
321
|
+
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
|
322
|
+
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
|
323
|
+
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
|
324
|
+
// @ts-nocheck
|
|
325
|
+
|
|
326
|
+
import { modules } from "./controller/**/{controller,*.controller}.ts#default";
|
|
327
|
+
|
|
328
|
+
export const getControllers = (): Class<unknown>[] => {
|
|
329
|
+
return modules.map((mod) => mod.default as Class<unknown>);
|
|
330
|
+
};
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
1. Point `Sapling.resolve` to `getControllers`
|
|
334
|
+
|
|
335
|
+
[example file](https://github.com/tahminator/instalock-web/blob/main/apps/server/src/index.ts#L45-L47)
|
|
336
|
+
|
|
337
|
+
```ts
|
|
338
|
+
const controllers = getControllers();
|
|
339
|
+
console.log(`${controllers.length} controllers resolved`);
|
|
340
|
+
controllers.map(Sapling.resolve).forEach((r) => app.use(r));
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
1. Configure your ESBuild process to use the `esbuild-plugin-import-pattern` plugin
|
|
344
|
+
|
|
345
|
+
[example file](https://github.com/tahminator/instalock-web/blob/main/apps/server/build.ts)
|
|
346
|
+
|
|
347
|
+
```ts
|
|
348
|
+
import * as esbuild from "esbuild";
|
|
349
|
+
// @ts-expect-error no types
|
|
350
|
+
import { importPatternPlugin } from "esbuild-plugin-import-pattern";
|
|
351
|
+
|
|
352
|
+
async function main() {
|
|
353
|
+
const ctx = await esbuild.context({
|
|
354
|
+
entryPoints: ["src/index.ts"],
|
|
355
|
+
bundle: true,
|
|
356
|
+
sourcemap: true,
|
|
357
|
+
platform: "node",
|
|
358
|
+
outfile: "src/index.js",
|
|
359
|
+
logLevel: "info",
|
|
360
|
+
format: "cjs",
|
|
361
|
+
plugins: [importPatternPlugin()],
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
await ctx.rebuild();
|
|
365
|
+
await ctx.dispose();
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
void main();
|
|
369
|
+
```
|
|
370
|
+
|
|
285
371
|
## License
|
|
286
372
|
|
|
287
373
|
MIT
|
package/dist/src/html/404.js
CHANGED