pcafstockf / async-injection Goto Github PK
View Code? Open in Web Editor NEWA robust lightweight dependency injection library for TypeScript.
License: MIT License
A robust lightweight dependency injection library for TypeScript.
License: MIT License
When calling Binder.resolveSingletons(), an unavailable constructor argument on a synchronous singleton does not result in resolveSingletons throwing a Map of Errors (which it should).
The imports in ESM build appear to be missing .js
extensions, so when I try to import it from a project that uses ESM, I get the following error:
node:internal/errors:490
ErrorCaptureStackTrace(err);
^
Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/xxxxxx/node_modules/async-injection/lib/esm/container' imported from /xxxxxx/node_modules/async-injection/lib/esm/index.js
at new NodeError (node:internal/errors:399:5)
at finalizeResolution (node:internal/modules/esm/resolve:326:11)
at moduleResolve (node:internal/modules/esm/resolve:945:10)
at defaultResolve (node:internal/modules/esm/resolve:1153:11)
at nextResolve (node:internal/modules/esm/loader:163:28)
at ESMLoader.resolve (node:internal/modules/esm/loader:838:30)
at ESMLoader.getModuleJob (node:internal/modules/esm/loader:424:18)
at ModuleWrap.<anonymous> (node:internal/modules/esm/module_job:77:40)
at link (node:internal/modules/esm/module_job:76:36) {
code: 'ERR_MODULE_NOT_FOUND'
}
Hi,
i have the following problem:
When i try to use injection in my jest-tests I am getting undefined for all dependencies -> No Symbol not found, but plain undefined.
Basic example:
import { Container, Inject, Injectable } from 'async-injection';
import 'reflect-metadata';
@Injectable()
class TestM {
constructor(@Inject('TEST') public t: number) {}
}
test('basic injection', async () => {
const c = new Container();
c.bindConstant('TEST', 1);
c.bindClass(TestM);
const test = await c.resolve(TestM);
expect(test.t).toBe(1);
});
Output:
FAIL core packages/core/src/lib/core.test.ts
✕ basic injection (3 ms)
● basic injection
expect(received).toBe(expected) // Object.is equality
Expected: 1
Received: undefined
14 | const test = await c.resolve(TestM);
15 |
> 16 | expect(test.t).toBe(1);
| ^
17 | });
18 |
at Object.<anonymous> (src/lib/core.test.ts:16:18)
Test Suites: 1 failed, 1 total
Tests: 1 failed, 1 total
Snapshots: 0 total
Time: 0.288 s, estimated 1 s
Ran all test suites.
TS-Config:
{
"compileOnSave": false,
"compilerOptions": {
"rootDir": ".",
"sourceMap": true,
"declaration": false,
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"importHelpers": true,
"target": "ES2022",
"module": "esnext",
"lib": ["es2022", "dom"],
"skipLibCheck": true,
"skipDefaultLibCheck": true,
"baseUrl": ".",
"paths": {
...
}
},
"exclude": ["node_modules", "tmp"]
}
Jest Config:
export default {
displayName: 'core',
preset: '../../jest.preset.js',
testEnvironment: 'node',
transform: {
'^.+\\.[tj]sx?$': [
'@swc/jest',
{
jsc: {
parser: {
syntax: 'typescript',
tsx: false,
decorators: true,
dynamicImport: true,
},
transform: { react: { runtime: 'automatic' } },
},
},
],
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
coverageDirectory: '../../coverage/packages/core',
};
Any idea on this? Thanks for your help :)
Proposal of new decorators.
This decorators are only syntax sugar, they will work with any container.
@Factory
- Syntax sugar for bindFactory
@Factory(factory: SyncFactory<T>)
// Default
@Factory(() => new FactoryExample())
class FactoryExample{}
// Will run the Factory.
container.bindClass(FactoryExample);
@AsyncFactory
- Syntax sugar for bindAsyncFactory
@AsyncFactory(factory: AsyncFactory<T>)
// Default
@AsyncFactory(async () => {
const instance = new FactoryExample();
await instance.connect();
return instance;
})
class AsyncFactoryExample{
connect(): Promise<void> { ... }
}
// Create the bind.
container.bindClass(AsyncFactoryExample);
// Now it's the same as Asynchronous Usage.
Note: I'm pretty new to DI; this may be entirely expected behaviour or I may be thinking about it the wrong way, it just seemed a little counterintuitive to me!
Basically, I'm trying to use this alongside MikroORM, which expects you to create a new "entity manager" object for each request. I assumed I could do something like
@Injectable()
class SomeClassUsingOrm {
constructor(private readonly entityManager: EntityManager) {}
}
rootContainer.bindClass(SomeClassUsingOrm)
// on each request
const child = new Container(rootContainer)
child.bindFactory(EntityManager, () => makeNewEntityManager()).asSingleton()
console.log(child.get(SomeClassUsingOrm)) // expecting this to use the factory from the child
However, this gives a Error: Symbol not bound: class EntityManager
error. It seems like because I used bindClass
on the parent, the EntityManager
parameter will only be resolved with the parent's providers and will ignore anything added to the child.
My goal here is to have a "abstract container" with a set of classes that depend on an type I can't yet construct. Or, to put it another way, I want a singleton factory that's called exactly once per clone of the parent container.
Add an option to clone the container.
Example:
For each request in a server, clone the container for specific request binds.
const container = new Container();
container.bind(ExampleRepository).asSingleton();
app.use((req, res, next) => {
req.container = container.clone();
// Just an Example.
req.container.bindFactory(Request, () => req).asSingleton();
req.container.bindFactory(Response, () => res).asSingleton();
});
@pcafstockf i can make the pull request if you like the idea.
I updated my dependencies and now webpack displays an error like this:
WARNING in ./src/scripts/node/event-dispatcher.ts 63:15-21
export 'Inject' (imported as 'Inject') was not found in 'async-injection' (module has no exports)
@ ./src/scripts/main.ts 12:0-58 27:28-43 28:32-47
ERROR in ./node_modules/async-injection/lib/esm/index.js 1:0-40
Module not found: Error: Can't resolve './container' in 'C:\Coding\gen-world\node_modules\async-injection\lib\esm'
Did you mean 'container.js'?
BREAKING CHANGE: The request './container' failed to resolve only because it was resolved as fully specified
(probably because the origin is strict EcmaScript Module, e. g. a module with javascript mimetype, a '*.mjs' file, or a '*.js' file where the package.json contains '"type": "module"').
The extension in the request is mandatory for it to be fully specified.
Add the extension to the request.
resolve './container' in 'C:\Coding\gen-world\node_modules\async-injection\lib\esm'
using description file: C:\Coding\gen-world\node_modules\async-injection\lib\esm\package.json (relative path: .)
Field 'browser' doesn't contain a valid alias configuration
using description file: C:\Coding\gen-world\node_modules\async-injection\lib\esm\package.json (relative path: ./container)
Field 'browser' doesn't contain a valid alias configuration
C:\Coding\gen-world\node_modules\async-injection\lib\esm\container doesn't exist
@ ./src/scripts/main.ts 11:0-44 22:30-39
ERROR in ./node_modules/async-injection/lib/esm/index.js 2:0-75
Module not found: Error: Can't resolve './decorators' in 'C:\Coding\gen-world\node_modules\async-injection\lib\esm'
Did you mean 'decorators.js'?
BREAKING CHANGE: The request './decorators' failed to resolve only because it was resolved as fully specified
(probably because the origin is strict EcmaScript Module, e. g. a module with javascript mimetype, a '*.mjs' file, or a '*.js' file where the package.json contains '"type": "module"').
The extension in the request is mandatory for it to be fully specified.
Add the extension to the request.
resolve './decorators' in 'C:\Coding\gen-world\node_modules\async-injection\lib\esm'
using description file: C:\Coding\gen-world\node_modules\async-injection\lib\esm\package.json (relative path: .)
Field 'browser' doesn't contain a valid alias configuration
using description file: C:\Coding\gen-world\node_modules\async-injection\lib\esm\package.json (relative path: ./decorators)
Field 'browser' doesn't contain a valid alias configuration
C:\Coding\gen-world\node_modules\async-injection\lib\esm\decorators doesn't exist
@ ./src/scripts/main.ts 11:0-44 22:30-39
As a workaround, I added a rule for webpack:
rules: [
{
test: /async-injection/,
resolve: {
fullySpecified: false
}
},
// ...
],
Presumably the new behavior of webpack is related to:
webpack/webpack#11467
In my project im using @abraham/reflection
, i want to keep it and use this package as well, its possible to remove reflect-metadata
and let the user select the reflection polyfill?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.