@slimlib/injector 1.0.6 → 2.0.0

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
@@ -2,79 +2,146 @@
2
2
 
3
3
  Parameter names based dependency injector for nodejs.
4
4
 
5
- *Limitations*
5
+ [Changelog](./CHANGELOG.md)
6
+
7
+ ## API
8
+
9
+ ### createInject()
6
10
 
7
- - does not work with default parameters
8
- - minification of code is not supported
11
+ Returns a new instance of an injector function to work with.
12
+
13
+ _Limitations_
14
+
15
+ - minification of code is not supported (use `createInjectAnnotated` instead)
9
16
  - not typesafe
10
- - classes not supported
11
17
  - slower than a normal function call
12
18
 
13
- [Changelog](./CHANGELOG.md)
19
+ ### createInjectAnnotated()
14
20
 
15
- ## API
21
+ Returns a new instance of an injector function that uses AngularJS-style array annotation for minification safety.
16
22
 
17
- ### createInject()
23
+ Instead of parsing parameter names, it expects dependencies to be specified as strings in an array before the function:
24
+
25
+ ```typescript
26
+ inject(["dep1", "dep2", (dep1, dep2) => { ... }]);
27
+ ```
18
28
 
19
- returns a new instance of an injector function to work with.
29
+ This allows code to be minified since the dependency names are preserved as strings.
30
+
31
+ > **Note:** The input array is mutated during injection (the function is removed via `pop()`). Always use inline array literals or create a fresh array for each call if you need to reuse the dependency list.
20
32
 
21
33
  ### injector(function, scope)
22
34
 
23
- injects arguments into function and invokes it
35
+ Injects arguments into function and invokes it.
24
36
 
25
- `function` - *required*, function to inject parameters and call
26
- `scope` - *optional*, *default* = `{}`, this argument for the function
37
+ `function` - _required_, function to inject parameters and call (or annotated array for `createInjectAnnotated`)
38
+ `scope` - _optional_, _default_ = `{}`, this argument for the function
27
39
 
28
40
  ### $provide(key, value)
29
41
 
30
- predefined injectable function
42
+ Predefined injectable function.
31
43
 
32
44
  `key` - string, required
33
45
  `value` - unknown
34
46
 
35
- to get it, inject it into the function
47
+ To get it, inject it into the function:
36
48
 
37
49
  ```typescript
38
50
  inject(($provide: Provider) => {
39
- $provide('service', service);
51
+ $provide("service", service);
40
52
  });
41
53
  ```
42
54
 
43
- ## Example
55
+ ## Examples
56
+
57
+ ### Using createInject (development / non-minified)
44
58
 
45
59
  ```typescript
46
- import createInject from '@slimlib/injector';
60
+ import { createInject } from "@slimlib/injector";
47
61
 
48
62
  const inject = createInject();
49
63
 
50
64
  inject(($provide: Provider) => {
51
- $provide('config', {
52
- url: 'http://example.com/json',
53
- format: 'json'
54
- });
65
+ $provide("config", {
66
+ url: "http://example.com/json",
67
+ format: "json",
68
+ });
55
69
  });
56
70
 
57
71
  inject(async (config: Json) => {
72
+ const data = await fetch(config.url);
73
+ const result = config.json ? await data.json() : data;
74
+ // and so on
75
+ });
76
+ ```
77
+
78
+ ### Using createInjectAnnotated (minification-safe)
79
+
80
+ ```typescript
81
+ import { createInjectAnnotated } from "@slimlib/injector";
82
+
83
+ const inject = createInjectAnnotated();
84
+
85
+ inject([
86
+ "$provide",
87
+ ($provide: Provider) => {
88
+ $provide("config", {
89
+ url: "http://example.com/json",
90
+ format: "json",
91
+ });
92
+ },
93
+ ]);
94
+
95
+ inject([
96
+ "config",
97
+ async (config: Json) => {
58
98
  const data = await fetch(config.url);
59
99
  const result = config.json ? await data.json() : data;
60
100
  // and so on
101
+ },
102
+ ]);
103
+ ```
104
+
105
+ This style is similar to AngularJS's dependency injection annotation:
106
+
107
+ ```javascript
108
+ // Before minification
109
+ angular.module("App", []).controller("MyController", function ($scope) {
110
+ $scope.value = "test";
111
+ });
112
+
113
+ // After minification (broken)
114
+ angular.module("App", []).controller("MyController", function (a) {
115
+ a.value = "test";
61
116
  });
117
+
118
+ // With array annotation (works after minification)
119
+ angular.module("App", []).controller("MyController", [
120
+ "$scope",
121
+ function (a) {
122
+ a.value = "test";
123
+ },
124
+ ]);
62
125
  ```
63
126
 
127
+ ## Build-time Swapping
128
+
129
+ You can use `createInject` during development and swap to `createInjectAnnotated` at build time for production. This can be done with build tools like Rollup, Webpack, or esbuild by aliasing the import.
130
+
64
131
  # FAQ
65
132
 
66
133
  1. Is it a good solution to mock something in unit tests?
67
134
 
68
- - no, please use [jest](https://jestjs.io/), [vitest](https://vitest.dev/), [proxyquire](https://www.npmjs.com/package/proxyquire), [proxyrequire](https://www.npmjs.com/package/proxyrequire) and other similar approaches to mock modules.
135
+ - No, please use [jest](https://jestjs.io/), [vitest](https://vitest.dev/), [proxyquire](https://www.npmjs.com/package/proxyquire), [proxyrequire](https://www.npmjs.com/package/proxyrequire) and other similar approaches to mock modules.
69
136
 
70
137
  2. Is it a good solution to use in frontend code?
71
138
 
72
- - no, it will not work after minification
139
+ - `createInject` will not work after minification, but `createInjectAnnotated` is designed to work with minified code.
73
140
 
74
141
  3. Is it good for nodejs applications?
75
142
 
76
- - only in some edge cases, please use singletons/factories/something else if possible
143
+ - Only in some edge cases, please use singletons/factories/something else if possible.
77
144
 
78
145
  # License
79
146
 
80
- [MIT](https://github.com/kshutkin/slimlib/blob/main/LICENSE)
147
+ [MIT](https://github.com/kshutkin/slimlib/blob/main/LICENSE)
package/package.json CHANGED
@@ -1,27 +1,25 @@
1
1
  {
2
2
  "type": "module",
3
- "version": "1.0.6",
3
+ "version": "2.0.0",
4
4
  "name": "@slimlib/injector",
5
5
  "description": "Parameter names based dependency injector for nodejs",
6
6
  "license": "MIT",
7
7
  "author": "Konstantin Shutkin",
8
- "main": "./dist/index.cjs",
9
- "module": "./dist/index.mjs",
8
+ "main": "./src/index.js",
9
+ "module": "./src/index.js",
10
10
  "exports": {
11
11
  ".": {
12
- "types": "./dist/index.d.ts",
13
- "import": "./dist/index.mjs",
14
- "require": "./dist/index.cjs",
15
- "default": "./dist/index.mjs"
12
+ "types": "./types/index.d.ts",
13
+ "default": "./src/index.js"
16
14
  },
17
15
  "./package.json": "./package.json"
18
16
  },
19
- "types": "./dist/index.d.ts",
17
+ "types": "./types/index.d.ts",
20
18
  "files": [
21
- "dist"
19
+ "types"
22
20
  ],
23
- "engines": {
24
- "node": ">=15"
21
+ "dependencies": {
22
+ "@slimlib/get-parameter-names": "^1.0.0"
25
23
  },
26
24
  "repository": {
27
25
  "type": "git",
@@ -36,8 +34,5 @@
36
34
  "injector",
37
35
  "dependency injection",
38
36
  "nodejs"
39
- ],
40
- "dependencies": {
41
- "get-parameter-names": "0.3.0"
42
- }
37
+ ]
43
38
  }
package/src/index.js ADDED
@@ -0,0 +1,58 @@
1
+ import { getParameterNames } from '@slimlib/get-parameter-names';
2
+
3
+ /**
4
+ * @typedef {(key: string, value: unknown) => void} Provider
5
+ */
6
+
7
+ /**
8
+ * @typedef {[...string[], (...args: any[]) => any]} AnnotatedFunction
9
+ */
10
+
11
+ /**
12
+ * @returns {<F extends (...args: any[]) => any>(func: F, scope?: object) => ReturnType<F>}
13
+ */
14
+ export function createInject() {
15
+ /** @type {Record<string, unknown>} */
16
+ const dependencies = Object.create(null);
17
+
18
+ dependencies.$provide = /** @type {Provider} */ (
19
+ (key, value) => {
20
+ dependencies[key] = value;
21
+ }
22
+ );
23
+
24
+ return (func, scope = {}) =>
25
+ func.apply(
26
+ scope,
27
+ getParameterNames(func).map((/** @type {string} */ key) => dependencies[key])
28
+ );
29
+ }
30
+
31
+ /**
32
+ * Creates an injector that uses AngularJS-style array annotation for minification safety.
33
+ * Instead of parsing parameter names, it expects dependencies to be specified as strings
34
+ * in an array before the function: ['dep1', 'dep2', function(dep1, dep2) { ... }]
35
+ * @returns {<F extends (...args: any[]) => any>(funcOrArray: F | [...string[], F], scope?: object) => ReturnType<F>}
36
+ */
37
+ export function createInjectAnnotated() {
38
+ /** @type {Record<string, unknown>} */
39
+ const dependencies = Object.create(null);
40
+
41
+ dependencies.$provide = /** @type {Provider} */ (
42
+ (key, value) => {
43
+ dependencies[key] = value;
44
+ }
45
+ );
46
+
47
+ return (funcOrArray, scope = {}) => {
48
+ if (Array.isArray(funcOrArray)) {
49
+ const func = /** @type {(...args: any[]) => any} */ (funcOrArray.pop());
50
+ return func.apply(
51
+ scope,
52
+ /** @type {string[]} */ (funcOrArray).map(key => dependencies[key])
53
+ );
54
+ }
55
+ // If not an array, just call the function with no dependencies
56
+ return funcOrArray.apply(scope, []);
57
+ };
58
+ }
@@ -0,0 +1,15 @@
1
+ declare module '@slimlib/injector' {
2
+ export function createInject(): <F extends (...args: any[]) => any>(func: F, scope?: object) => ReturnType<F>;
3
+ /**
4
+ * Creates an injector that uses AngularJS-style array annotation for minification safety.
5
+ * Instead of parsing parameter names, it expects dependencies to be specified as strings
6
+ * in an array before the function: ['dep1', 'dep2', function(dep1, dep2) { ... }]
7
+ * */
8
+ export function createInjectAnnotated(): <F extends (...args: any[]) => any>(funcOrArray: F | [...string[], F], scope?: object) => ReturnType<F>;
9
+ export type Provider = (key: string, value: unknown) => void;
10
+ export type AnnotatedFunction = [...string[], (...args: any[]) => any];
11
+
12
+ export {};
13
+ }
14
+
15
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,17 @@
1
+ {
2
+ "version": 3,
3
+ "file": "index.d.ts",
4
+ "names": [
5
+ "createInject",
6
+ "createInjectAnnotated",
7
+ "Provider",
8
+ "AnnotatedFunction"
9
+ ],
10
+ "sources": [
11
+ "../src/index.js"
12
+ ],
13
+ "sourcesContent": [
14
+ null
15
+ ],
16
+ "mappings": ";iBAagBA,YAAYA;;;;;;iBAuBZC,qBAAqBA;aAjCeC,QAAQA;aAIPC,iBAAiBA"
17
+ }
package/dist/index.cjs DELETED
@@ -1,15 +0,0 @@
1
- 'use strict';
2
-
3
- var getParameterNames = require('get-parameter-names');
4
-
5
- var index = () => {
6
- const dependencies = Object.create(null);
7
- dependencies['$provide'] = (key, value) => {
8
- dependencies[key] = value;
9
- };
10
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
11
- return (func, scope = {}) => func.apply(scope, getParameterNames(func)
12
- .map((key) => dependencies[key]));
13
- };
14
-
15
- module.exports = index;
package/dist/index.d.ts DELETED
@@ -1,3 +0,0 @@
1
- export type Provider = (key: string, value: unknown) => void;
2
- declare const _default: () => <F extends (...args: any[]) => any>(func: F, scope?: object) => ReturnType<F>;
3
- export default _default;
package/dist/index.mjs DELETED
@@ -1,13 +0,0 @@
1
- import getParameterNames from 'get-parameter-names';
2
-
3
- var index = () => {
4
- const dependencies = Object.create(null);
5
- dependencies['$provide'] = (key, value) => {
6
- dependencies[key] = value;
7
- };
8
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
9
- return (func, scope = {}) => func.apply(scope, getParameterNames(func)
10
- .map((key) => dependencies[key]));
11
- };
12
-
13
- export { index as default };