Skip to content

Commit

Permalink
Merge pull request #117 from opensumi/next
Browse files Browse the repository at this point in the history
  • Loading branch information
bytemain authored Jan 19, 2024
2 parents becf999 + 0e356bd commit 72fc9df
Show file tree
Hide file tree
Showing 55 changed files with 687 additions and 397 deletions.
6 changes: 6 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,11 @@ module.exports = {
allowedNames: ['self', 'injector'],
},
],
'no-void': [
'error',
{
allowAsStatement: true,
},
],
},
};
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
# 由 https://github.com/msfeldstein/gitignore 自动生成
lib
esm
types

# Logs
logs
*.log
Expand Down
15 changes: 15 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/** @type {import('ts-jest').JestConfigWithTsJest} */
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
coveragePathIgnorePatterns: ['/node_modules/', '/test/'],
coverageThreshold: {
global: {
branches: 10,
functions: 10,
lines: 10,
statements: 10,
},
},
setupFilesAfterEnv: ['<rootDir>/scripts/jest-setup.ts'],
};
30 changes: 8 additions & 22 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,23 @@
"version": "1.10.1",
"description": "A dependency injection tool for Javascript.",
"license": "MIT",
"main": "dist/index.js",
"module": "esm/index.js",
"main": "lib/index.js",
"types": "types/index.d.ts",
"scripts": {
"prepare": "husky install",
"lint": "eslint . --ext .js,.jsx,.ts,.tsx",
"build": "rm -rf dist && tsc -p tsconfig.prod.json",
"test": "jest --coverage test/**",
"build": "npm run build:lib && npm run build:esm",
"build:lib": "rm -rf lib && tsc -p tsconfig.lib.json",
"build:esm": "rm -rf esm && tsc -p tsconfig.esm.json",
"test": "jest --coverage tests/**",
"test:watch": "yarn test --watch",
"ci": "npm run lint && npm run test",
"prepublishOnly": "npm run build",
"prerelease": "npm run lint && npm run test && npm run build",
"release": "commit-and-tag-version --npmPublishHint 'echo Just Push code to remote repo, npm publish will be done by CI.'",
"release:beta": "npm run release -- --prerelease beta"
},
"dependencies": {
"reflect-metadata": "^0.1.13"
},
"devDependencies": {
"@commitlint/cli": "17.2.0",
"@commitlint/config-conventional": "17.2.0",
Expand All @@ -34,6 +35,7 @@
"jest": "29.2.2",
"lint-staged": "13.0.3",
"prettier": "2.7.1",
"reflect-metadata": "^0.1.13",
"ts-jest": "29.0.3",
"typescript": "4.8.4"
},
Expand All @@ -55,22 +57,6 @@
"@commitlint/config-conventional"
]
},
"jest": {
"preset": "ts-jest",
"testEnvironment": "node",
"coveragePathIgnorePatterns": [
"/node_modules/",
"/test/"
],
"coverageThreshold": {
"global": {
"branches": 10,
"functions": 10,
"lines": 10,
"statements": 10
}
}
},
"keywords": [
"di",
"injector"
Expand Down
1 change: 1 addition & 0 deletions scripts/jest-setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import 'reflect-metadata';
2 changes: 1 addition & 1 deletion src/helper/compose.ts → src/compose.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ function dispatch<C>(

stack.depth = idx;

let maybePromise: Promise<void> | void;
let maybePromise: Promise<void> | void | undefined;

if (idx < middlewareList.length) {
const middleware = middlewareList[idx];
Expand Down
52 changes: 30 additions & 22 deletions src/decorator.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
import 'reflect-metadata';
import * as Helper from './helper';
import * as Error from './error';
import {
Token,
InstanceOpts,
Expand All @@ -13,31 +10,42 @@ import {
IAfterReturningAspectHookFunction,
IAfterThrowingAspectHookFunction,
IAroundHookOptions,
} from './declare';
import { markAsAspect, markAsHook } from './helper';
} from './types';
import {
addDeps,
getInjectorOfInstance,
getParameterDeps,
isToken,
markAsAspect,
markAsHook,
markInjectable,
setParameterIn,
setParameters,
} from './helper';
import { noInjectorError, notInjectError, tokenInvalidError } from './error';

/**
* 装饰一个 Class 是否是可以被依赖注入
* Decorate a Class to mark it as injectable
* @param opts
*/
export function Injectable(opts?: InstanceOpts): ClassDecorator {
return <T extends Function>(target: T) => {
Helper.markInjectable(target, opts);
markInjectable(target, opts);

const params = Reflect.getMetadata('design:paramtypes', target);
if (Array.isArray(params)) {
Helper.setParameters(target, params);
setParameters(target, params);

// 如果支持多例创建,就不检查构造函数依赖的可注入性
// If it supports multiple instances, do not check the injectability of the constructor dependencies
if (opts && opts.multiple) {
return;
}

// 检查依赖的可注入性
const depTokens = Helper.getParameterDeps(target);
// Check the injectability of the constructor dependencies
const depTokens = getParameterDeps(target);
depTokens.forEach((item, index) => {
if (!Helper.isToken(item)) {
throw Error.notInjectError(target, index);
if (!isToken(item)) {
throw notInjectError(target, index);
}
});
}
Expand All @@ -46,7 +54,7 @@ export function Injectable(opts?: InstanceOpts): ClassDecorator {

interface InjectOpts {
/**
* 默认值
* Default value when the token is not found
*/
default?: any;
}
Expand All @@ -57,7 +65,7 @@ interface InjectOpts {
*/
export function Inject(token: Token, opts: InjectOpts = {}): ParameterDecorator {
return (target, _: string | symbol | undefined, index: number) => {
Helper.setParameterIn(target, { ...opts, token }, index);
setParameterIn(target, { ...opts, token }, index);
};
}

Expand All @@ -67,7 +75,7 @@ export function Inject(token: Token, opts: InjectOpts = {}): ParameterDecorator
*/
export function Optional(token: Token = Symbol()): ParameterDecorator {
return (target, _: string | symbol | undefined, index: number) => {
Helper.setParameterIn(target, { default: undefined, token }, index);
setParameterIn(target, { default: undefined, token }, index);
};
}

Expand All @@ -84,22 +92,22 @@ export function Autowired(token?: Token, opts?: InstanceOpts): PropertyDecorator
realToken = Reflect.getMetadata('design:type', target, propertyKey);
}

if (!Helper.isToken(realToken)) {
throw Error.tokenInvalidError(target, propertyKey, realToken);
if (!isToken(realToken)) {
throw tokenInvalidError(target, propertyKey, realToken);
}

// 添加构造函数的依赖
Helper.addDeps(target, realToken);
// Add the dependency of the constructor
addDeps(target, realToken);

const descriptor: PropertyDescriptor = {
configurable: true,
enumerable: true,
get(this: any) {
if (!this[INSTANCE_KEY]) {
const injector = Helper.getInjectorOfInstance(this);
const injector = getInjectorOfInstance(this);

if (!injector) {
throw Error.noInjectorError(this);
throw noInjectorError(this);
}

this[INSTANCE_KEY] = injector.get(realToken, opts);
Expand Down
7 changes: 6 additions & 1 deletion src/error.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Context, Token } from './declare';
import { Context, Token } from './types';

function stringify(target: object | Token) {
if (typeof target === 'object') {
Expand Down Expand Up @@ -65,3 +65,8 @@ export function aliasCircularError(paths: Token[], current: Token) {
`useAlias registration cycle detected! ${[...paths, current].map((v) => stringify(v)).join(' -> ')}`,
);
}

export function noInstancesInCompletedCreatorError(token: Token) {
/* istanbul ignore next */
return new Error(`Cannot find value of ${stringify(token)} in a completed creator.`);
}
2 changes: 1 addition & 1 deletion src/factoryHelper.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FactoryFunction } from './declare';
import { FactoryFunction } from './types';

import type { Injector } from './injector';

Expand Down
4 changes: 2 additions & 2 deletions src/helper/dep-helper.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { flatten, uniq } from './util';
import { Token } from '../declare';
import { flatten, uniq } from './utils';
import { Token } from '../types';
import { getParameterDeps } from './parameter-helper';
import { createConstructorMetadataManager } from './reflect-helper';

Expand Down
28 changes: 21 additions & 7 deletions src/helper/event.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
export class EventEmitter<T> {
private _listeners: Map<T, Function[]> = new Map();
/**
* Modified from https://github.com/opensumi/utils/blob/main/packages/events/src/index.ts
*/

on(event: T, listener: Function) {
export type Handler<T extends any[]> = (...args: T) => void;

export class EventEmitter<Events extends Record<any, any[]>> {
private _listeners: Map<keyof Events, any[]> = new Map();

on<Event extends keyof Events>(event: Event, listener: Handler<Events[Event]>) {
if (!this._listeners.has(event)) {
this._listeners.set(event, []);
}
Expand All @@ -10,7 +16,7 @@ export class EventEmitter<T> {
return () => this.off(event, listener);
}

off(event: T, listener: Function) {
off<Event extends keyof Events>(event: Event, listener: Handler<Events[Event]>) {
if (!this._listeners.has(event)) {
return;
}
Expand All @@ -21,22 +27,30 @@ export class EventEmitter<T> {
}
}

once(event: T, listener: Function) {
const remove: () => void = this.on(event, (...args: any[]) => {
once<Event extends keyof Events>(event: Event, listener: Handler<Events[Event]>) {
const remove: () => void = this.on(event, (...args: Parameters<Handler<Events[Event]>>) => {
remove();
listener.apply(this, args);
});

return remove;
}

emit(event: T, ...args: any[]) {
emit<Event extends keyof Events>(event: Event, ...args: Parameters<Handler<Events[Event]>>) {
if (!this._listeners.has(event)) {
return;
}
[...this._listeners.get(event)!].forEach((listener) => listener.apply(this, args));

Check warning on line 43 in src/helper/event.ts

View workflow job for this annotation

GitHub Actions / ci

Forbidden non-null assertion
}

hasListener<Event extends keyof Events>(event: Event) {
return this._listeners.has(event);
}

getListeners<Event extends keyof Events>(event: Event) {
return this._listeners.get(event) || [];
}

dispose() {
this._listeners.clear();
}
Expand Down
Loading

0 comments on commit 72fc9df

Please sign in to comment.