@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 CHANGED
@@ -3,39 +3,47 @@
3
3
  [![BACKEND COVERAGE](https://img.shields.io/sonar/coverage/tahminator_sapling?server=https%3A%2F%2Fsonarcloud.io&style=flat&label=TEST%20COVERAGE)](https://sonarcloud.io/dashboard?id=tahminator_sapling)
4
4
  [![NPM Version](https://img.shields.io/npm/v/%40tahminator%2Fsapling?label=NPM%20LATEST)](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
- - [Controllers](#controllers)
17
- - [HTTP Methods](#http-methods)
18
- - [Responses](#responses)
19
- - [Error Handling](#error-handling)
20
- - [Middleware](#middleware)
21
- - [Redirects](#redirects)
22
- - [Dependency Injection](#dependency-injection)
23
- - [Custom Serialization](#custom-serialization)
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 great, but it can get really messy really quickly. Sapling lets you define controllers and routes using decorators instead of manually wiring everything up.
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. I took a lot of inspiration from Spring, but I don't believe that it would be correct to try to force Express.js or Typescript to adopt OOP entirely, which leads me to my next point:
30
- - The best reason to use Sapling is that you can also eject out of the object oriented environment and run regular functional Express.js without having to do anything extra or hacky.
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. I prefer Sapling to other libraries like Nest.js because they are usually too heavy of an abstraction. I only want what would be helpful to improve development speed without getting in my way, nothing more & (ideally) nothing else.
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 600+ 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).
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
@@ -1,8 +1,7 @@
1
1
  /**
2
2
  * Default Express.js 404 error page, as a string.
3
3
  */
4
- export const Html404ErrorPage = (error) => `
5
- <!DOCTYPE html>
4
+ export const Html404ErrorPage = (error) => `<!DOCTYPE html>
6
5
  <html lang="en">
7
6
  <head>
8
7
  <meta charset="utf-8">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tahminator/sapling",
3
- "version": "1.5.22",
3
+ "version": "1.5.24",
4
4
  "author": "Tahmid Ahmed",
5
5
  "description": "A library to help you write cleaner Express.js code",
6
6
  "repository": {