Skip to content

Commit

Permalink
feat: nicer syntax for resolveWith
Browse files Browse the repository at this point in the history
closes #78
  • Loading branch information
Jack Ellis committed Aug 23, 2020
1 parent 6ecb1c3 commit d2ec9b2
Show file tree
Hide file tree
Showing 6 changed files with 120 additions and 7 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Change Log
- changed format of `.factory` `.service` and `.resolve`
- you can now pass an `opts` parameter when registering a factory i.e. `.factory<A>(fn, { lifecycle: 'none' })`
- you can now pass an `opts` parameter when resolving i.e. `.resolve<A>({ optional: true })`
- `resolveWith` now has a nicer syntax for ts inference: `.resolveWith<Foo, Dep1, Dep2>([ 'val1', 'val2' ])`. The original syntax i.e. `.resolveWith({ dep1: 'val1' })` is still valid.

#### Breaking Changes
- if you attempt to resolve a global like `Window` without registering it first, rather than throw an error, you will now get the global variable
Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
"module": "dist/es/jpex.js",
"types": "dist/es/index.d.ts",
"scripts": {
"test": "ava",
"test:debug": "ava debug",
"coverage": "nyc ava",
"test": "BABEL_DISABLE_CACHE=1 ava",
"test:debug": "BABEL_DISABLE_CACHE=1 ava debug",
"coverage": "BABEL_DISABLE_CACHE=1 nyc ava",
"lint": "eslint './src/**/*.ts' && eslint './plugin/**/*.ts' && tsc --noEmit",
"build:prepare": "rm -rf dist",
"build:js": "rollup --config ./rollup.config.js",
Expand Down
26 changes: 26 additions & 0 deletions plugin/resolveWith.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,33 @@ const resolveWith = (
args.unshift(t.stringLiteral(name));
} else if (t.isTSTypeLiteral(type) || t.isTSFunctionType(type)) {
throw new Error('Currently resolving with a literal type is not supported');
} else {
return;
}

if (!t.isArrayExpression(args[1])) {
return;
}

const namedDependencies: t.ObjectProperty[] = [];
let i = 1;
let namedType = getTypeParameter(path, i);
while (namedType) {
const name = getConcreteTypeName(namedType, filename, publicPath, programPath);
if (name != null) {
const value = args[1].elements[i - 1];
const key = t.stringLiteral(name);
// @ts-ignore
const prop = t.objectProperty(key, value);
namedDependencies.push(prop);
} else if (t.isTSTypeLiteral(type) || t.isTSFunctionType(type)) {
throw new Error('Currently resolving with a literal type is not supported');
}
// eslint-disable-next-line no-plusplus
namedType = getTypeParameter(path, ++i);
}

args.splice(1, 1, t.objectExpression(namedDependencies));
};

export default resolveWith;
6 changes: 4 additions & 2 deletions src/__tests__/resolve.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,10 @@ test('resolves named dependencies', (t) => {
type Named = string;

jpex.factory<Foo>((named: Named) => named);
const result = jpex.resolveWith<Foo>({
[jpex.infer<Named>()]: 'pop',
const result = jpex.resolve<Foo>({
with: {
[jpex.infer<Named>()]: 'pop',
},
});

t.is(result, 'pop');
Expand Down
78 changes: 78 additions & 0 deletions src/__tests__/resolveWith.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import anyTest, { TestInterface } from 'ava';
import base, { JpexInstance } from '..';

const test: TestInterface<{
jpex: JpexInstance,
}> = anyTest;

test.beforeEach((t) => {
const jpex = base.extend();

t.context = {
jpex,
};
});

test('it resolves with given values (js)', (t) => {
const { jpex } = t.context;

jpex.factory('A', [ 'B', 'C', 'D' ], (b, c, d) => {
return b + c + d;
});

const result = jpex.resolveWith('A', {
B: 'b',
C: 'c',
D: 'd',
});

t.is(result, 'bcd');
});

test('it resolves with given values (ts)', (t) => {
const { jpex } = t.context;

type A = string;
type B = string;
type C = string;
type D = string;

jpex.factory<A>((b: B, c: C, d: D) => b + c + d);

const result = jpex.resolveWith<A>({
[jpex.infer<B>()]: 'b',
[jpex.infer<C>()]: 'c',
[jpex.infer<D>()]: 'd',
});

t.is(result, 'bcd');
});

test('it resolves using type inference (1)', (t) => {
const { jpex } = t.context;
type A = string;
type B = string;

jpex.factory<A>((b: B) => `a${b}`);

const result = jpex.resolveWith<A, B>([ 'b' ]);

t.is(result, 'ab');
});

test('it resolves with type inference (6)', (t) => {
const { jpex } = t.context;
type A = string;
type B = string;
type C = string;
type D = string;
type E = string;
type F = string;
type G = string;

jpex.factory<A>((b: B, c: C, d: D, e: E, f: F, g: G) => `a${b}${c}${d}${e}${f}${g}`);

const result = jpex.resolveWith<A, B, C, D, E, F, G>([ 'b', 'c', 'd', 'e', 'f', 'g' ]);

t.is(result, 'abcdefg');
});
10 changes: 8 additions & 2 deletions src/types/JpexInstance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,14 @@ export interface JpexInstance {
resolve(name: Dependency, opts?: ResolveOpts): any,
resolve<T>(opts?: ResolveOpts): T,

resolveWith(name: Dependency, namedParameters: NamedParameters): any
resolveWith<T>(namedParameters: NamedParameters): T,
resolveWith(name: Dependency, namedParameters: NamedParameters, opts?: ResolveOpts): any
resolveWith<T>(namedParameters: NamedParameters, opts?: ResolveOpts): T,
resolveWith<T, A>(args: [ A ], opts?: ResolveOpts): T,
resolveWith<T, A, B>(args: [ A, B ], opts?: ResolveOpts): T,
resolveWith<T, A, B, C>(args: [ A, B, C ], opts?: ResolveOpts): T,
resolveWith<T, A, B, C, D>(args: [ A, B, C, D ], opts?: ResolveOpts): T,
resolveWith<T, A, B, C, D, E>(args: [ A, B, C, D, E ], opts?: ResolveOpts): T,
resolveWith<T, A, B, C, D, E, F>(args: [ A, B, C, D, E, F ], opts?: ResolveOpts): T,

encase<F extends AnyFunction<AnyFunction>>(
dependencies: Dependency[],
Expand Down

0 comments on commit d2ec9b2

Please sign in to comment.