Skip to content

Commit

Permalink
feat: new injection package
Browse files Browse the repository at this point in the history
  • Loading branch information
hubert authored and aceHubert committed Jun 26, 2024
1 parent fc03180 commit 0738a8d
Show file tree
Hide file tree
Showing 10 changed files with 388 additions and 2 deletions.
2 changes: 2 additions & 0 deletions packages/injects/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Injections

49 changes: 49 additions & 0 deletions packages/injects/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{
"name": "@ace-util/injects",
"version": "0.0.0",
"author": "Hubert<yi.xiang@live.com>",
"description": "Utils.",
"main": "lib/index.js",
"module": "esm/index.js",
"umd:main": "dist/index.umd.production.js",
"unpkg": "dist/index.umd.production.js",
"jsdelivr": "dist/index.umd.production.js",
"keywords": [
"ace-util",
"injects",
"hooks"
],
"files": [
"dist",
"lib",
"esm"
],
"scripts": {
"prebuild": "node -p \"'export const version: string = ' + JSON.stringify(require('./package.json').version) + ';'\" > src/version.ts",
"serve": "tsc --project tsconfig.build.json --module es2015 --declaration --declarationDir types --outDir esm --watch",
"build": "rimraf -rf lib esm dist types && yarn build:cjs && yarn build:esm && yarn build:umd",
"build:cjs": "tsc --project tsconfig.build.json",
"build:esm": "tsc --project tsconfig.build.json --module es2015 --outDir esm",
"build:umd": "rollup --config",
"lint": "eslint . --cache --report-unused-disable-directives --ignore-path=../../.eslintignore",
"lint:fix": "eslint . --cache --fix --ignore-path=../../.eslintignore"
},
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/aceHubert/ace-util.git",
"directory": "packages/injects"
},
"bugs": {
"url": "https://github.com/aceHubert/ace-util/issues"
},
"dependencies": {
"warning": "^4.0.3"
},
"devDependencies": {
"@types/warning": "^3.0.3"
},
"publishConfig": {
"access": "public"
}
}
3 changes: 3 additions & 0 deletions packages/injects/rollup.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import baseConfig from '../../scripts/rollup.base';

export default baseConfig('index', 'AceUtil.Injects', {}, false);
170 changes: 170 additions & 0 deletions packages/injects/src/Inject.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
import warning from 'warning';
import { hasOwn, isObject } from '@ace-util/core';

export type Callback = {
func: Function;
acceptedArgs: number;
};

// function sortBy(name: string, order: 'ASC' | 'DESC' = 'ASC') {
// return function(o: Dictionary<any>, p: Dictionary<any>) {
// let a, b;
// if (typeof o === 'object' && typeof p === 'object' && o && p) {
// a = o[name];
// b = p[name];
// if (a === b) {
// return 0;
// }
// if (typeof a === typeof b) {
// const result = a < b ? -1 : 1;
// return order === 'DESC' ? ~result + 1 : result;
// }
// const result = typeof a < typeof b ? -1 : 1;
// return order === 'DESC' ? ~result + 1 : result;
// } else {
// throw 'error';
// }
// };
// }

export class Inject {
static buildPreinitializedStorage(filters: Record<string, InstanceType<typeof Inject> | Record<number, Callback[]>>) {
const normalized: Record<string, InstanceType<typeof Inject>> = {};

for (const tag in filters) {
const filter = filters[tag];
if (isObject(filter) && filter instanceof Inject) {
normalized[tag] = filter;
continue;
}

const inject = new Inject();
for (const priority in filter) {
const cbs = filter[priority] as Callback[];
cbs.forEach((cb: Callback) => {
inject.addFilter(cb.func, priority as unknown as number, cb.acceptedArgs);
});
}

normalized[tag] = inject;
}

return normalized;
}

private callbacks: { [priority: number]: Callback[] } = {};
private doingAction = false;

get filters() {
return this.callbacks;
}

/**
* Check if any filter has been registered
* @since
*
*/
public hasFilter(functionToCheck: Function | boolean = false): Function | boolean {
const funcs = Object.entries(this.callbacks).reduce((prev, [_priority, callbacks]) => {
return prev.concat(callbacks);
}, [] as Callback[]);

if (typeof functionToCheck === 'boolean') {
return funcs.length > 0;
} else {
const cb = funcs.find(({ func }) => func === functionToCheck);
return cb ? cb.func : false;
}
}

/**
* Add filter
* @since
*
*/
public addFilter(callback: Function, priority = 10, acceptedArgs = 1): void {
if (!hasOwn(this.callbacks, priority as any)) {
this.callbacks[priority] = [];
}

this.callbacks[priority].push({
func: callback,
acceptedArgs,
});
}

/**
* Remove filter
* @since
*
*/
public removeFilter(functionToRemove: Function, priority = 10): boolean {
if (hasOwn(this.callbacks, priority as any)) {
const index = this.callbacks[priority].findIndex(({ func }) => func === functionToRemove);
index >= 0 && this.callbacks[priority].splice(index, 1);
return index >= 0;
} else {
return false;
}
}

/**
* Remove filter
* @since
*
*/
public removeAllFilters(priority: boolean | number = false) {
if (typeof priority === 'boolean') {
this.callbacks = {};
} else if (hasOwn(this.callbacks, priority as any)) {
this.callbacks[priority] = [];
} else {
warning(
process.env.NDE_ENV === 'production',
`priority "${priority}" is not exists , no filiters will be removed.`,
);
}
}

public applyFilters<T = unknown, R = T>(value: T, ...args: unknown[]): Promise<R> {
const sortFuncs = Object.entries(this.callbacks)
.sort(([a], [b]) => Number(a) - Number(b))
.reduce((prev, [_priority, callbacks]) => prev.concat(callbacks), [] as Callback[]);

if (sortFuncs.length) {
const _doingAction = this.doingAction;

return new Promise((resolve) => exec(0, value, resolve));

// @ts-ignore
function exec(index: number, value: unknown, resolve: (result: any) => void) {
const cb = sortFuncs[index];
const _args = [...args];
if (!_doingAction) {
_args.unshift(value);
}
const result = cb.func.apply(null, _args.slice(0, cb.acceptedArgs));
if (index === sortFuncs.length - 1) {
result instanceof Promise ? result.then(resolve) : resolve(result);
} else if (result instanceof Promise) {
result.then((val) => exec(index + 1, val, resolve));
} else {
exec(index + 1, result, resolve);
}
}
}

return Promise.resolve(value as unknown as R);
}

public doAction(...args: unknown[]) {
this.doingAction = true;
return new Promise<void>((resolve, reject) => {
this.applyFilters('', ...args).then(() => {
//
this.doingAction = false;
resolve();
}, reject);
});
}
}
79 changes: 79 additions & 0 deletions packages/injects/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { hasOwn } from '@ace-util/core';
import { Inject } from './Inject';

// Types
import { InjectFunction, InjectResult } from './types';

/**
* create a new Inject instance
* @param store Store
*/
export function createInject(store: Record<string, InstanceType<typeof Inject>>): InjectFunction {
function inject(tag: string): InjectResult;
function inject(tag: string, functionToAdd: Function, priority?: number, acceptedArgs?: number): void;
function inject(
tag: string,
functionToAdd?: Function,
priority?: number,
acceptedArgs?: number,
): InjectResult | void {
if (functionToAdd) {
if (!hasOwn(store, tag)) {
store[tag] = new Inject();
}

store[tag].addFilter(functionToAdd, priority, acceptedArgs);
} else {
return {
has(functionToCheck: Function | boolean) {
if (!hasOwn(store, tag)) {
return false;
}

return store[tag].hasFilter(functionToCheck);
},
remove(functionToRemove: Function, priority = 10) {
let removed = false;
if (hasOwn(store, tag)) {
removed = store[tag].removeFilter(functionToRemove, priority);
}
return removed;
},
removeAll(priority: boolean | number) {
if (hasOwn(store, tag)) {
store[tag].removeAllFilters(priority);
}
},
filter<T = unknown, R = T>(value: T, ...args: unknown[]) {
if (hasOwn(store, 'all')) {
// todo:apply tag "all" filter
}

if (hasOwn(store, tag)) {
return store[tag].applyFilters<T, R>(value, ...args);
}

return Promise.resolve(value as unknown as R);
},
exec(...args: unknown[]) {
if (hasOwn(store, tag)) {
return store[tag].doAction(...args);
}
return Promise.resolve();
},
} as InjectResult;
}
}

return inject;
}

/**
* Global filters is used as singleton and can storage all inject callbacks;
*/
export const globalStorage = Inject.buildPreinitializedStorage({});

/**
* Global Inject instalce
*/
export const globalInject = createInject(globalStorage);
47 changes: 47 additions & 0 deletions packages/injects/src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/**
* Inject result
*/
export type InjectResult = {
/**
* Has function form injection
* @param functionToCheck Function to check
*/
has(functionToCheck?: Function | boolean): Function | boolean;
/**
* Remove function form injection
* @param functionToRemove Function to remove
* @param priority Priority
*/
remove(functionToRemove: Function, priority?: number): boolean;
/**
* Remove all functions form injection
* @param priority Priority
*/
removeAll(priority: boolean | number): void;
/**
* Filter from injection tag, will always have a return value
* @param value Value to filter
* @param args Arguments
*/
filter<T = unknown, R = T>(value: T, ...args: unknown[]): Promise<R>;

/**
* Execute from injection tag, won't have a return value
* @param args Arguments
*/
exec(...args: unknown[]): Promise<void>;
};

/**
* Injection instance
*/
export interface InjectFunction {
/**
* Execute a tag
*/
(tag: string): InjectResult;
/**
* Inject function to a tag
*/
(tag: string, functionToAdd: Function, priority?: number, acceptedArgs?: number): void;
}
1 change: 1 addition & 0 deletions packages/injects/src/version.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const version: string = "0.0.0";
7 changes: 7 additions & 0 deletions packages/injects/tsconfig.build.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./lib",
"declaration": true
}
}
9 changes: 9 additions & 0 deletions packages/injects/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"baseUrl": ".",
"skipLibCheck": false,
"lib": ["ESNext", "DOM"]
},
"include": ["./src/**/*.ts"],
}
Loading

0 comments on commit 0738a8d

Please sign in to comment.