@mstream/di
Advanced tools
Comparing version 1.1.1 to 2.0.0
@@ -1,1 +0,12 @@ | ||
{} | ||
{ | ||
"arrowParens": "always", | ||
"bracketSpacing": true, | ||
"embeddedLanguageFormatting": "auto", | ||
"endOfLine": "lf", | ||
"printWidth": 72, | ||
"quoteProps": "consistent", | ||
"semi": false, | ||
"tabWidth": 2, | ||
"trailingComma": "all", | ||
"useTabs": false | ||
} |
@@ -5,2 +5,15 @@ # Changelog | ||
## 2.0.0 (2023-01-18) | ||
### ⚠ BREAKING CHANGES | ||
* The "createContainer" function was renamed to "contextBuilder". | ||
* Context is not a member of the container any more, | ||
it is created by calling "build" method of the context builder. | ||
### Features | ||
* allow eager initialization of selected dependencies ([be1c99b](https://github.com/mstream/di/commit/be1c99b4e34b5d5f8d12e72039f41bb3e77c6727)) | ||
### 1.1.1 (2023-01-17) | ||
@@ -7,0 +20,0 @@ |
@@ -14,3 +14,3 @@ { | ||
"test:js": "vitest run --dir test", | ||
"test": "npm run test:js && npm start 2>/dev/null" | ||
"test": "npm run test:js && npm start" | ||
}, | ||
@@ -17,0 +17,0 @@ "author": "Maciej Laciak <maciej.laciak@gmail.com> (https://github.com/mstream/)", |
export default ({ logger }) => | ||
(message) => { | ||
logger.debug(message); | ||
logger.error(message); | ||
logger.info(message); | ||
logger.warning(message); | ||
}; | ||
logger.debug(message) | ||
logger.error(message) | ||
logger.info(message) | ||
logger.warning(message) | ||
} |
@@ -1,6 +0,6 @@ | ||
import { createContainer } from "@mstream/di"; | ||
import appCreator from "./app.js"; | ||
import loggerCreator, { WARNING } from "./logger.js"; | ||
import { contextBuilder } from "@mstream/di" | ||
import appCreator from "./app.js" | ||
import loggerCreator, { WARNING } from "./logger.js" | ||
createContainer() | ||
contextBuilder() | ||
.register(`console`, () => console) | ||
@@ -10,2 +10,3 @@ .register(`config`, () => ({ logLevel: WARNING })) | ||
.register(`app`, appCreator) | ||
.context.app(`Hello World!`); | ||
.build() | ||
.app(`Hello World!`) |
@@ -1,5 +0,5 @@ | ||
export const DEBUG = Symbol(`logLevel/DEBUG`); | ||
export const ERROR = Symbol(`logLevel/ERROR`); | ||
export const INFO = Symbol(`logLevel/INFO`); | ||
export const WARNING = Symbol(`logLevel/WARNING`); | ||
export const DEBUG = Symbol(`logLevel/DEBUG`) | ||
export const ERROR = Symbol(`logLevel/ERROR`) | ||
export const INFO = Symbol(`logLevel/INFO`) | ||
export const WARNING = Symbol(`logLevel/WARNING`) | ||
@@ -9,3 +9,3 @@ export default ({ config: { logLevel }, console }) => ({ | ||
if ([DEBUG].includes(logLevel)) { | ||
console.info(`[DEBUG] ${log}`); | ||
console.info(`[DEBUG] ${log}`) | ||
} | ||
@@ -15,3 +15,3 @@ }, | ||
if ([DEBUG, ERROR, INFO, WARNING].includes(logLevel)) { | ||
console.error(`[ERROR] ${log}`); | ||
console.error(`[ERROR] ${log}`) | ||
} | ||
@@ -21,3 +21,3 @@ }, | ||
if ([DEBUG, INFO, WARNING].includes(logLevel)) { | ||
console.error(`[WARNING] ${log}`); | ||
console.error(`[WARNING] ${log}`) | ||
} | ||
@@ -27,5 +27,5 @@ }, | ||
if ([DEBUG, INFO].includes(logLevel)) { | ||
console.info(`[INFO] ${log}`); | ||
console.info(`[INFO] ${log}`) | ||
} | ||
}, | ||
}); | ||
}) |
@@ -1,20 +0,20 @@ | ||
import sinon from "sinon"; | ||
import { describe, expect, it } from "vitest"; | ||
import appCreator from "../src/app.js"; | ||
import sinon from "sinon" | ||
import { describe, expect, it } from "vitest" | ||
import appCreator from "../src/app.js" | ||
describe(`app`, () => { | ||
it(`logs a message at every possible level`, () => { | ||
const debug = sinon.spy(); | ||
const error = sinon.spy(); | ||
const info = sinon.spy(); | ||
const warning = sinon.spy(); | ||
const debug = sinon.spy() | ||
const error = sinon.spy() | ||
const info = sinon.spy() | ||
const warning = sinon.spy() | ||
appCreator({ | ||
logger: { debug, error, info, warning }, | ||
})(`abc`); | ||
})(`abc`) | ||
expect(debug.calledWith(`abc`)).toBe(true); | ||
expect(error.calledWith(`abc`)).toBe(true); | ||
expect(info.calledWith(`abc`)).toBe(true); | ||
expect(warning.calledWith(`abc`)).toBe(true); | ||
}); | ||
}); | ||
expect(debug.calledWith(`abc`)).toBe(true) | ||
expect(error.calledWith(`abc`)).toBe(true) | ||
expect(info.calledWith(`abc`)).toBe(true) | ||
expect(warning.calledWith(`abc`)).toBe(true) | ||
}) | ||
}) |
@@ -1,138 +0,143 @@ | ||
import sinon from "sinon"; | ||
import { describe, expect, it } from "vitest"; | ||
import loggerCreator, { DEBUG, ERROR, INFO, WARNING } from "../src/logger.js"; | ||
import sinon from "sinon" | ||
import { describe, expect, it } from "vitest" | ||
import loggerCreator, { | ||
DEBUG, | ||
ERROR, | ||
INFO, | ||
WARNING, | ||
} from "../src/logger.js" | ||
describe(`logger`, () => { | ||
it(`debug log is printed when logging level is set to debug`, () => { | ||
const info = sinon.spy(); | ||
const info = sinon.spy() | ||
loggerCreator({ | ||
config: { logLevel: DEBUG }, | ||
console: { info }, | ||
}).debug(`abc`); | ||
}).debug(`abc`) | ||
expect(info.calledWith(`[DEBUG] abc`)).toBe(true); | ||
}); | ||
expect(info.calledWith(`[DEBUG] abc`)).toBe(true) | ||
}) | ||
it(`debug log is not printed when logging level is set to info`, () => { | ||
const info = sinon.spy(); | ||
const info = sinon.spy() | ||
loggerCreator({ | ||
config: { logLevel: INFO }, | ||
console: { info }, | ||
}).debug(`abc`); | ||
expect(info.called).toBe(false); | ||
}); | ||
}).debug(`abc`) | ||
expect(info.called).toBe(false) | ||
}) | ||
it(`debug log is not printed when logging level is set to warning`, () => { | ||
const info = sinon.spy(); | ||
const info = sinon.spy() | ||
loggerCreator({ | ||
config: { logLevel: WARNING }, | ||
console: { info }, | ||
}).debug(`abc`); | ||
expect(info.called).toBe(false); | ||
}); | ||
}).debug(`abc`) | ||
expect(info.called).toBe(false) | ||
}) | ||
it(`debug log is not printed when logging level is set to error`, () => { | ||
const info = sinon.spy(); | ||
const info = sinon.spy() | ||
loggerCreator({ | ||
config: { logLevel: ERROR }, | ||
console: { info }, | ||
}).debug(`abc`); | ||
expect(info.called).toBe(false); | ||
}); | ||
}).debug(`abc`) | ||
expect(info.called).toBe(false) | ||
}) | ||
it(`info log is printed when logging level is set to debug`, () => { | ||
const info = sinon.spy(); | ||
const info = sinon.spy() | ||
loggerCreator({ | ||
config: { logLevel: DEBUG }, | ||
console: { info }, | ||
}).info(`abc`); | ||
}).info(`abc`) | ||
expect(info.calledWith(`[INFO] abc`)).toBe(true); | ||
}); | ||
expect(info.calledWith(`[INFO] abc`)).toBe(true) | ||
}) | ||
it(`info log is printed when logging level is set to info`, () => { | ||
const info = sinon.spy(); | ||
const info = sinon.spy() | ||
loggerCreator({ | ||
config: { logLevel: INFO }, | ||
console: { info }, | ||
}).info(`abc`); | ||
expect(info.calledWith(`[INFO] abc`)).toBe(true); | ||
}); | ||
}).info(`abc`) | ||
expect(info.calledWith(`[INFO] abc`)).toBe(true) | ||
}) | ||
it(`info log is not printed when logging level is set to warning`, () => { | ||
const info = sinon.spy(); | ||
const info = sinon.spy() | ||
loggerCreator({ | ||
config: { logLevel: WARNING }, | ||
console: { info }, | ||
}).info(`abc`); | ||
expect(info.called).toBe(false); | ||
}); | ||
}).info(`abc`) | ||
expect(info.called).toBe(false) | ||
}) | ||
it(`info log is not printed when logging level is set to error`, () => { | ||
const info = sinon.spy(); | ||
const info = sinon.spy() | ||
loggerCreator({ | ||
config: { logLevel: ERROR }, | ||
console: { info }, | ||
}).info(`abc`); | ||
expect(info.called).toBe(false); | ||
}); | ||
}).info(`abc`) | ||
expect(info.called).toBe(false) | ||
}) | ||
it(`warning log is printed when logging level is set to debug`, () => { | ||
const error = sinon.spy(); | ||
const error = sinon.spy() | ||
loggerCreator({ | ||
config: { logLevel: DEBUG }, | ||
console: { error }, | ||
}).warning(`abc`); | ||
}).warning(`abc`) | ||
expect(error.calledWith(`[WARNING] abc`)).toBe(true); | ||
}); | ||
expect(error.calledWith(`[WARNING] abc`)).toBe(true) | ||
}) | ||
it(`warning log is printed when logging level is set to info`, () => { | ||
const error = sinon.spy(); | ||
const error = sinon.spy() | ||
loggerCreator({ | ||
config: { logLevel: INFO }, | ||
console: { error }, | ||
}).warning(`abc`); | ||
expect(error.calledWith(`[WARNING] abc`)).toBe(true); | ||
}); | ||
}).warning(`abc`) | ||
expect(error.calledWith(`[WARNING] abc`)).toBe(true) | ||
}) | ||
it(`warning log is printed when logging level is set to warning`, () => { | ||
const error = sinon.spy(); | ||
const error = sinon.spy() | ||
loggerCreator({ | ||
config: { logLevel: WARNING }, | ||
console: { error }, | ||
}).warning(`abc`); | ||
expect(error.calledWith(`[WARNING] abc`)).toBe(true); | ||
}); | ||
}).warning(`abc`) | ||
expect(error.calledWith(`[WARNING] abc`)).toBe(true) | ||
}) | ||
it(`warning log is not printed when logging level is set to error`, () => { | ||
const error = sinon.spy(); | ||
const error = sinon.spy() | ||
loggerCreator({ | ||
config: { logLevel: ERROR }, | ||
console: { error }, | ||
}).warning(`abc`); | ||
expect(error.called).toBe(false); | ||
}); | ||
}).warning(`abc`) | ||
expect(error.called).toBe(false) | ||
}) | ||
it(`error log is printed when logging level is set to debug`, () => { | ||
const error = sinon.spy(); | ||
const error = sinon.spy() | ||
loggerCreator({ | ||
config: { logLevel: DEBUG }, | ||
console: { error }, | ||
}).error(`abc`); | ||
}).error(`abc`) | ||
expect(error.calledWith(`[ERROR] abc`)).toBe(true); | ||
}); | ||
expect(error.calledWith(`[ERROR] abc`)).toBe(true) | ||
}) | ||
it(`error log is printed when logging level is set to info`, () => { | ||
const error = sinon.spy(); | ||
const error = sinon.spy() | ||
loggerCreator({ | ||
config: { logLevel: INFO }, | ||
console: { error }, | ||
}).error(`abc`); | ||
expect(error.calledWith(`[ERROR] abc`)).toBe(true); | ||
}); | ||
}).error(`abc`) | ||
expect(error.calledWith(`[ERROR] abc`)).toBe(true) | ||
}) | ||
it(`error log is printed when logging level is set to warning`, () => { | ||
const error = sinon.spy(); | ||
const error = sinon.spy() | ||
loggerCreator({ | ||
config: { logLevel: WARNING }, | ||
console: { error }, | ||
}).error(`abc`); | ||
expect(error.calledWith(`[ERROR] abc`)).toBe(true); | ||
}); | ||
}).error(`abc`) | ||
expect(error.calledWith(`[ERROR] abc`)).toBe(true) | ||
}) | ||
it(`error log is printed when logging level is set to error`, () => { | ||
const error = sinon.spy(); | ||
const error = sinon.spy() | ||
loggerCreator({ | ||
config: { logLevel: ERROR }, | ||
console: { error }, | ||
}).error(`abc`); | ||
expect(error.calledWith(`[ERROR] abc`)).toBe(true); | ||
}); | ||
}); | ||
}).error(`abc`) | ||
expect(error.calledWith(`[ERROR] abc`)).toBe(true) | ||
}) | ||
}) |
{ | ||
"name": "@mstream/di", | ||
"version": "1.1.1", | ||
"version": "2.0.0", | ||
"description": "", | ||
@@ -5,0 +5,0 @@ "private": false, |
@@ -1,12 +0,25 @@ | ||
export function createContainer() { | ||
const cache = {}; | ||
const context = {}; | ||
export function contextBuilder() { | ||
let wasBuilt = false | ||
const cache = {} | ||
const context = {} | ||
const eagerMembers = [] | ||
function build() { | ||
wasBuilt = true | ||
eagerMembers.forEach((name) => context[name]) | ||
return context | ||
} | ||
function register(name, creator) { | ||
if (wasBuilt) { | ||
throw Error(`The context has already been built.`) | ||
} | ||
if (typeof creator !== `function`) { | ||
throw Error(`"${name}" creator is not a function`); | ||
throw Error(`The creator of "${name}" is not a function.`) | ||
} | ||
if (name in context) { | ||
throw Error(`"${name}" is already registered in the context`); | ||
throw Error(`The "${name}" is already registered in the context.`) | ||
} | ||
@@ -17,13 +30,19 @@ | ||
if (!(name in cache)) { | ||
cache[name] = creator(context); | ||
cache[name] = creator(context) | ||
} | ||
return cache[name]; | ||
return cache[name] | ||
}, | ||
}); | ||
}) | ||
return this; | ||
return this | ||
} | ||
return { context, register }; | ||
function registerEager(name, creator) { | ||
eagerMembers.push(name) | ||
register(name, creator) | ||
return this | ||
} | ||
return { build, register, registerEager } | ||
} |
@@ -1,50 +0,69 @@ | ||
import { describe, expect, it } from "vitest"; | ||
import { createContainer } from "../src"; | ||
import { describe, expect, it } from "vitest" | ||
import { contextBuilder } from "../src" | ||
describe(`createContainer()`, () => { | ||
describe(`contextBuilder`, () => { | ||
it(`allows to register and invoke creators`, () => { | ||
const { string } = createContainer().register( | ||
`string`, | ||
() => `abc` | ||
).context; | ||
const { foo } = contextBuilder() | ||
.register(`foo`, () => 1) | ||
.build() | ||
expect(string).toBe(`abc`); | ||
}); | ||
expect(foo).toBe(1) | ||
}) | ||
it(`does not call a creator until requested`, () => { | ||
const { string } = createContainer() | ||
const { foo } = contextBuilder() | ||
.register(`error`, () => { | ||
throw Error(); | ||
throw Error() | ||
}) | ||
.register(`string`, () => `abc`).context; | ||
.register(`foo`, () => 1) | ||
.build() | ||
expect(string).toBe(`abc`); | ||
}); | ||
expect(foo).toBe(1) | ||
}) | ||
it(`when registered as eager, calls creator imidiately on context creation`, () => { | ||
expect(() => | ||
contextBuilder() | ||
.registerEager(`error`, () => { | ||
throw Error(`error`) | ||
}) | ||
.build(), | ||
).toThrow(/error/) | ||
}) | ||
it(`allow registring creators in any order`, () => { | ||
const { string } = createContainer() | ||
.register(`string`, ({ number }) => `abc${number}`) | ||
.register(`number`, () => 1).context; | ||
const { foo } = contextBuilder() | ||
.register(`foo`, ({ bar }) => bar + 1) | ||
.register(`bar`, () => 1) | ||
.build() | ||
expect(string).toBe(`abc1`); | ||
}); | ||
expect(foo).toBe(2) | ||
}) | ||
it(`fails when creator is not a function`, () => { | ||
expect(() => createContainer().register(`string`, `abc`)).toThrow( | ||
/"string" creator is not a function/ | ||
); | ||
}); | ||
expect(() => contextBuilder().register(`foo`, 1)).toThrow( | ||
/The creator of "foo" is not a function/, | ||
) | ||
}) | ||
it(`prevents from overriding creators`, () => { | ||
const container = createContainer().register(`string`, () => `abc`); | ||
expect(() => container.register(`string`, () => `def`)).toThrow( | ||
/"string" is already registered in the context/ | ||
); | ||
}); | ||
expect(() => | ||
contextBuilder() | ||
.register(`foo`, () => 1) | ||
.register(`foo`, () => 1), | ||
).toThrow(/The "foo" is already registered in the context/) | ||
}) | ||
it(`does not execute creators more than once`, () => { | ||
let counter = 0; | ||
const { context } = createContainer().register(`string`, () => { | ||
counter += 1; | ||
return `abc`; | ||
}); | ||
context.string; | ||
expect(context.string).toBe(`abc`); | ||
expect(counter).toBe(1); | ||
}); | ||
}); | ||
let counter = 0 | ||
const context = contextBuilder() | ||
.register(`foo`, () => { | ||
counter += 1 | ||
return 1 | ||
}) | ||
.build() | ||
context.foo | ||
expect(context.foo).toBe(1) | ||
expect(counter).toBe(1) | ||
}) | ||
}) |
Sorry, the diff of this file is not supported yet
174522
4666