- Fix CJS environments (e.g. Jest + esbuild) failing to
require()the.mjsnative loader by providing separate CJS and ESM versions ofload-module-native(#418, #419)
- Fix ESM interop by renaming
load-module-native.jstoload-module-native.mjsso Node.js treats it as ESM regardless of the packagetypefield (#416, #417)
- Fix
importModuleexport by migrating frommodule.exportstoexport(reported & fix suggested in #413 by Vinícius Oliveira, fixed in #414) - Update packages
- Bump supported Node version to 20 (this should have been done in the latest major)
No runtime changes — all breaking changes are TypeScript-only.
- BREAKING (types): Default cradle type changed from
anyto{} - BREAKING (types):
register()returns a new container type that includes the registered resolvers - BREAKING (types):
asValue()now preserves literal types and deep readonly for objects
createContainer() without an explicit generic now returns AwilixContainer<{}> instead of AwilixContainer<any>. This enables type accumulation via register() chaining (any & T collapses to any, while {} & T produces T).
If you have existing code that relies on the any default (e.g. accessing container.cradle.something without type annotations), pass any explicitly:
// Before (v12) — implicit any
const container = createContainer()
container.cradle.anything // OK
// After (v13) — explicit any to preserve old behavior
const container = createContainer<any>()
container.cradle.anything // OK
// After (v13) — recommended: use register() for type inference
const container = createContainer().register({
anything: asValue('hello'),
})
container.cradle.anything // OK, typed as 'hello'register() now returns AwilixContainer<Cradle & InferCradleFromResolvers<R>> instead of this. Types accumulate across chained calls:
const container = createContainer()
.register({ port: asValue(3000) }) // AwilixContainer<{ port: 3000 }>
.register({ host: asValue('localhost') }) // AwilixContainer<{ port: 3000; host: 'localhost' }>
container.resolve('port') // 3000
container.resolve('host') // 'localhost'asValue() now uses a const type parameter, which means it preserves literal types for primitives and infers deep readonly for objects:
// Before (v12)
asValue(3000) // Resolver<number>
asValue('localhost') // Resolver<string>
asValue({ debug: true }) // Resolver<{ debug: boolean }>
// After (v13)
asValue(3000) // Resolver<3000>
asValue('localhost') // Resolver<'localhost'>
asValue({ debug: true }) // Resolver<{ readonly debug: true }>Resolved object values are now correctly typed as readonly, reflecting the fact that mutating container-owned values was never a safe practice:
const container = createContainer().register({
config: asValue({ debug: true }),
})
const config = container.resolve('config')
config.debug = false // Error: Cannot assign to 'debug' because it is a read-only property- Performance optimizations: cache-first resolve for singleton/scoped, closure-free cycle detection, and injector proxy key deduplication (#407 by @kibertoad)
- Fix symbol-keyed registrations being invisible to enumeration on the cradle and injector proxy (#407 by @kibertoad)
- #396 Use built-in
camelCaseimplementation, removing thecamel-casedependency (#405 by @kibertoad)
- Fix parameter parsing for classes by improving
constructorscanning heuristics - Update packages
- Add
react-nativeexport key, move upbrowserkey - Update packages
- Use default import for
fast-glob
- Rename
awilix.browser.jstoawilix.browser.mjs, most tools prefer this - More
exportsconditions
- Fix browser build
- BREAKING: Bump TypeScript transpilation target for browser build from
ES5toES2020 - Update packages
- Add
exportsfield topackage.json, allowing you to explicitly import the browser build usingimport { createContainer } from 'awilix/browser'
- Undo all
exportschanges as it broke downstream consumers
- Add
libto exports lists
- Add
defaultentry in every export
- Add "exports" field to package.json
- BREAKING: Drop Node 14 support, add Node 22 to build matrix.
- Migrate to ESLint v9
- Upgrade packages
Node 14 is no longer officially supported, as it is not an active LTS anymore.
- Add back
createBuildResolverandcreateDisposableResolverexports (#358)
- Add back some type exports (#351)
- Add (optional, off by default) strict mode to enforce extra correctness checks in both resolution and registration (#349 by @fnimick)
- Reduce the publicly accessible API surface to only that which is needed to use Awilix. This is potentially a breaking change if you were using any of the internal type definitions (#349 by @fnimick)
- Upgrade packages
- Fix
aliasToto allow passingsymbol(#342 by @zb-sj) - Migrate from TSLint to ESLint
- BREAKING CHANGE: Drop Node 12 support for real this time (updated the
enginesfield)
- Upgrade packages
- Move CI from Travis to GitHub Actions
- BREAKING: Drop Node 12 support, add Node 18 to build matrix
- BREAKING: #300 Rename
awilix.module.jstoawilix.module.mjs - #293 Update packages, including
fast-glob
Node 12 is no longer officially supported, as it is not an active LTS anymore.
I am not sure if this is actually a breaking change, but if someone is importing the module directly using the file name, then it would be.
- Use
LoadedModuleDescriptorinNameFormatterdelegate parameter - Update packages
- #291 Fix
listModulesregression on Windows - Update packages
- #288 Don't use
Error.captureStackTraceon unsupported platforms
- BREAKING: #286 Support
Symbol.toStringTag. This should fix es5-shim/480. - Update packages
The Awilix Cradle's string representation when used by util.inspect, .toJSON() and others returned [AwilixContainer.cradle]. This has been
changed to [object AwilixContainerCradle] to align with other common string representations in JavaScript.
When using Object.prototype.toString.call(container.cradle), it would return [object Object]. With this change, it now returns [object AwilixContainerCradle].
- #284 Use
fast-globinstead ofglob(Sebastian Plaza) - Update packages
Please see the list of breaking changes below.
- Update packages
- BREAKING: #198 Don't parse parameters from base class
- #270 Fix exponential performance slowdown for large containers
- This was done by not relying on
rollUpRegistrationsin theresolvepath. As a trade-off, performance for iterating thecradleproxy has degraded in order to guarantee accuracy. We consider this acceptable as iterating thecradleis not something one should be doing for anything besides debugging. Thanks to @code-ape for the diagnosis and for coming up with a fix!
- This was done by not relying on
- The
container.registrationsgetter on a scoped container no longer rolls up registrations from its' parent. - In
CLASSICmode, when parsing the constructor of a derived class, Awilix will no longer parse the base class' constructor in case the derived class' defined constructor does not define any arguments. However, if the derived class does not define a constructor, then Awilix will parse the base class' constructor. Please keep in mind that this only works for native classes, as Awilix works on thetoStringrepresentation of the class/function in order to determine when a class with no defined constructor is encountered. - Renamed
container.hastocontainer.hasRegistrationto avoid ambiguity. Does it have a registration? Does it have a cached module? Who knows? Let's gooo!
- Improve internal
uniqfunction performance by using aSet(#253, Anderson Leite) - Update packages
- Improve resolve typing (#247, Goran Mržljak)
- Update packages
Dropped Node 10 support. Minimum supported Node version is now 12.
- Move rollup-plugin-copy to devDependencies (#236, Evan Sosenko)
- Update packages
- Convert paths to file URLs in
loadModuleswith ESM, fixes #225. (#227, Jamie Corkhill)
GlobWithOptionsnow includesBuildResolverOptionsinstead ofResolverOptions(fixes #214)
- Add support for Native Node ES modules on Node v14+ (#211, Richard Simko)
- Fixes AwilixResolutionError throwing TypeError if resolution stack contains symbols (#205, astephens25)
- Update packages
- Fix return type for
createScopewhen using a cradle typing. (#182, moltar) - Remove
yarn.lock, contributing docs now recommendnpm. - Update packages, upgrade to Prettier 2.0
- Add optional generic parameter to container typing. Allows for a typed
ICradle. (#169, roikoren755)
- Fix issue with parsing comments (#165, reported by Jamie Corkhill)
- Fix issue where calling
JSON.stringifyon the cradle would result in memory leak (#153, berndartmueller) - Update packages
- Fix issue where the tokenizer was being too eager. (#30)
- Make tests pass on Node v12
- Update packages
- Fix
registeroption inloadModules(#124) - Update packages
- Add
hasmethod to container to check for an existing registration (#119, faustbrian)
- Extract dependencies from base class when no parameters were extracted. This works for ES6 classes as well as the old-school prototype approach to inheritance. Uses
Object.getPrototypeOf. (#107) - Allow auto-loading of named exports that expose a
RESOLVERsymbol prop. (#115)
- Support returning the
cradleinasyncfunctions (#109, andyfleming)) - Update packages
- [BREAKING CHANGE]: Scoped containers no longer use the parent's cache for
Lifetime.SCOPEDregistrations (#92) - Change the
"react-native"module path to useawilix.browser.js(#104) - Update packages
Awilix v4 corrects a misunderstanding in how the scoped caching should work. For full context, please see issue #92, but the TL:DR version is that prior v4, scoped registrations (Lifetime.SCOPED / .scoped()) would travel up the family tree and use a parent's cached resolution if there is any. If there is not, the resolving scope would cache it locally.
While this was by design, it was not very useful, and it was designed based on a misunderstanding of how Unity's HierarchicalLifetimeManager works. In v4, Lifetime.SCOPED now works the same way: the container performing the resolution also caches it, not looking outside itself for cached resolutions of Lifetime.SCOPED registrations.
A prime use case for this is having a scoped logger, as well as a root logger. This is actually what prompted this change.
// logger.js
export class Logger {
constructor(loggerPrefix = 'root') {
this.prefix = loggerPrefix
}
log(message) {
console.log(`[${this.prefix}]: ${message}`)
}
}// app.js
import { Logger } from './logger'
import { createContainer, asClass, InjectionMode } from 'awilix'
const container = createContainer({
injectionMode: InjectionMode.CLASSIC,
}).register({
logger: asClass(Logger).scoped(),
})
const scope = container.createScope()
scope.register({
loggerPrefix: asValue('dope scope'),
})
const rootLogger = container.resolve('logger')
const scopeLogger = scope.resolve('logger')
rootLogger.log('yo!') // [root]: yo!
scopeLogger.log('wassup!') // [dope scope]: wassup!Prior to v4, the scopeLogger would have resolved to the same as rootLogger because it would ask it's ancestors if they had a logger cached.
Now it works as you would probably expect it to: it keeps it's own cache.
- Updated packages.
- Add support for parsing async and generator functions; these no longer break the parser. (#90)
- Update dependencies.
- Skip code comments in parser (#87)
- Make the parser smarter by including full member expression paths so we get less false positives when scanning for the constructor token.
- Updated dependencies
- Fix TS 2.7 compilation issue
- Fix the
GlobWithOptionstype to includeLifetimeType
- Fix #76: don't overwrite declarations when building with Rollup.
- Adjust Rollup config to use latest config format
- Updated packages, fix an internal typing issue as a result of updated typings.
- Use
Reflect.construct()instead ofnewinternally; fixes TS transpilation issue. - Add note on browser support to README.
A lot of cool stuff has made it into version 3, and a few things were broken in the process. I have done my best to list everything here.
With v3 comes a few new cool features.
Disposer support (#48)
This has been a very requested feature. The idea is you can tell Awilix how to
dispose of a dependency—for example, to close a database connection—when calling
container.dispose().
const pg = require('pg')
const { createContainer, asFunction } = require('awilix')
const container = createContainer()
.register({
pool: (
asFunction(() => new pg.Pool({ ... }))
.singleton()
.disposer((pool) => pool.end())
)
})
// .. later, but only if a `pool` was ever created
container.dispose().then(() => {
console.log('One disposable connection.. disposed! Huehehehe')
})alias resolver (#55)
This new resolver lets you alias a registration. This is best illustrated with an example:
const { alias, asValue, createContainer } = require('awilix')
const container = createContainer()
container.register({
laughingOutLoud: asValue('hahahahaha'),
lol: alias('laughingOutLoud'),
})
container.resolve('lol') // 'hahahahaha'It's essentially the exact same as calling
container.resolve('laughingOutLoad'), but lol might be easier to type out in
your constructors. 😎
Default values in constructors/functions (#46)
This is a pretty small feature but was the most difficult to land, mainly because I had to write a smarter parser and tokenizer, not to mention they are now way better at skipping over code. Check out the tests, it's pretty wild.
class MyClass {
constructor(db, timeout = 1000) { /*...*/ }
}
container.register({
db: asFunction(..)
})
// Look! No errors!! :D
container.build(MyClass) instanceof MyClass // trueOfficial support for running in the browser (#69)
Awilix now ships with 4 module flavors: CommonJS (same old), ES Modules for Node, ES Modules for the Browser and UMD.
Please see the Universal Module section in the readme for details.
The following is a list of known breaking changes. If there's any I've missed feel free to let me know.
The entire library is now written in TypeScript! (#49)
This means a bunch of interfaces have been renamed and made more correct. If you're a TypeScript user, this is great news for you. 😄
ResolutionMode is now InjectionMode (#57)
ResolutionMode.jsrenamed toinjection-mode.tsResolutionModerenamed toInjectionMode
"Registrations" are now "Resolvers" (#51)
The terminology is now "you register a resolver to a name".
- TypeScript interfaces renamed
REGISTRATIONsymbol renamed toRESOLVERregistrations.jsrenamed toresolvers.tsregistrationOptionsinloadModulesrenamed toresolverOptions
registerClass, registerFunction and registerValue removed (#60)
This was done to simplify the API surface, and also simplifies the
implementation greatly (less overloads). You should be using
container.register with asClass, asFunction and asValue instead.
Resolver configuration chaining API is now immutable (#62)
This simplifies the TypeScript types and is also considered a good practice. All
configuration functions rely on this, meaning you should not do:
// I don't know why you would, but DONT DO THIS!
const singleton = asClass(MyClass).singleton
singleton()However, this also means you can now "split" a resolver to configure it differently. For example:
class GenericSender {
constructor(transport) {
this.transport = transport
}
send() {
if (this.transport === 'email') {
// ... etc
}
}
dispose() { /*...*/ }
}
const base = asClass(GenericSender).scoped().disposer((g) => g.dispose())
const emailSender = base.inject(() => ({ transport: 'email' })
const pushSender = base.inject(() => ({ transport: 'push' })
container.register({
emailSender,
pushSender
})Removed AwilixNotAFunctionError in favor of a generic AwilixTypeError (#52)
This should not have an impact on userland code but I thought I'd mention it.
There are a bunch of internal uses of this error, so I thought it made sense to consolidate them into one error type.
- Code is now formatted with Prettier
- Awilix is now using
husky+lint-stagedto lint, format and test every commit to ensure top code quality. - Switched to Jest from Mocha
- Switched from eslint to tslint
- Rewrote the function parameter parser, it is now much better at correctly skipping over default value expressions to reach the next parameter.
- Most (if not all) of the code is now documented and should be readable.
- Deprecated the
registerFunction,registerValueandregisterClassshortcuts.
- Fix typings for
container.build
- Add support for
container.build()- see relevant docs
- Add support for
Object.keys()on the cradle; now returns the names of available modules that can be resolved by accessing them.- There's a gotcha though;
Object.getOwnPropertyDescriptor()will return a gibberish descriptor. This is required for the keys to show up in the result.
- There's a gotcha though;
- Fix iterating over cradle - generator now yields registration names, thanks @neerfri! (#40)
- Fix issue with
console.logon the cradle throwing an error. (#7)- This should not break anything, but just to be safe I did a minor version bump.
- Add support for
Symbols (although not recommended).
- Change
RegistrationOptionstyping to union of string and options
- Fix typing for
REGISTRATIONsymbol
- Fix typing for
loadModules— it didn't allow the shortcut version of['glob.js', Lifetime.SCOPED] - Add Prettier formatting as well as
lint-stagedto keep the tests passing and the code fresh before committing.
- Remove
is-plain-objectandis-string, use simple checks instead. Trying to keep the dependencies as thin as possible.
- [NEW]: Support inline registration options (#34)
- [FIXED]:
container.loadModules()typing fix, thanks @dboune!
- [BREAKING]: Custom
isClassfunction that will treatfunction Capital () {}as a class due to the capital first letter of the function name. This is to improve compatibility with Babel's ES5 code generator, and is also a pretty commonly accepted standard naming convention. (#28) - [NEW]: Added support for passing in a
registerfunction toloadModules. (#28)
- [FIXED]: Parsing regression in 2.6.1 (#30)
- [FIXED]: Implemented a crude arguments parser to replace regex. (#30)
- [NEW]: infer function name for
registerClass/registerFunction(#26) - [FIXED]: Corrected some TypeScript typings related to
registerClassandregisterFunction.
- [NEW]: Implemented per-module locals injection (#24).
- Fixed issue where passing a
Lifetimelike.registerFunction('name', func, Lifetime.SCOPED)didn't work. - Documented
asClass,asValueandasFunction. - [FIXED]: nasty options leaking when using
registerClass/Function({ test1: [Test1, { }], test2: [Test2, { }] }).
- [BREAKING]: Guard assertions added to
asFunctionandasClass. This will prevent some nasty runtime behaviors. (#20), thanks @zer0tonin!
- [NEW]: Classic dependency resolution mode using parameter name matching implemented, thanks to @cjhoward92! This is an alternative to the default proxy mechanism.
- [BREAKING]: The
registerX({ name: [value, options]})pattern is not needed forregisterValuebecause it is so simple is requires no configuration. It was causing trouble when attempting to register an array as a value, because theregisterfunction would think it was the value-options-array pattern when it shouldn't be. This change is breaking in the sense that it solves the unexpected behavior, but it breaks existing registrations that would register arrays by usingregisterValue({ name: [[1, 2]] })(multi-dimensional array to work around the pre-2.3.0 behavior) - [chore]: Updated packages.
- Pass in the module descriptor to
formatName- thanks @anasinnyk! - Fixed some issues with TypeScript definitions.
- Fixed
registerFunctionreturn type definition - thanks @ycros!
- TypeScript definitions - thanks @blove!
- Webpack 2 compatibility - thanks @ewrogers!
console.loging the container will, instead of throwing an error, display a string summary of the container. Fixes #7.- started logging changes to a changelog (sorry about being so late to the party)