-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
388 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
# Injections | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export const version: string = "0.0.0"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
{ | ||
"extends": "./tsconfig.json", | ||
"compilerOptions": { | ||
"outDir": "./lib", | ||
"declaration": true | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"], | ||
} |
Oops, something went wrong.