@danep/utils
Advanced tools
| module.exports = { | ||
| root: true, | ||
| parserOptions: { | ||
| sourceType: 'module' | ||
| }, | ||
| env: { | ||
| browser: true, | ||
| node: true, | ||
| es6: true | ||
| }, | ||
| globals: { | ||
| it: 'readonly', | ||
| expect: 'readonly', | ||
| describe: 'readonly' | ||
| }, | ||
| extends: 'standard', | ||
| rules: { | ||
| indent: ['error', 2], | ||
| 'linebreak-style': ['error', 'unix'], | ||
| quotes: ['error', 'single'], | ||
| semi: ['error', 'always'] | ||
| } | ||
| }; |
| import { getValue } from './type.js'; | ||
| function createCache () { | ||
| let cache = null; | ||
| return { | ||
| get () { return cache; }, | ||
| set (val) { | ||
| cache = val; | ||
| return cache; | ||
| } | ||
| }; | ||
| } | ||
| /** | ||
| * @description 传入 promise 的创建函数, 返回一个包装函数, 用于缓存 promise 的值, 默认存储在 returnFn.cache | ||
| * @param {function} fn function: promise | ||
| * @param {function} cache.get 存储媒介 | ||
| * @param {function} cache.set 存储媒介 | ||
| * @returns {function} function: promise | ||
| */ | ||
| export function promiseCache (fn, cache = createCache()) { | ||
| promiseCacheWrap.cache = cache; | ||
| function promiseCacheWrap () { | ||
| const arg = arguments; | ||
| const cacheVal = cache.get(...arg); | ||
| if (cacheVal) return Promise.resolve(cacheVal); | ||
| else { | ||
| return Promise.resolve( | ||
| cache.set( | ||
| fn(...arg).catch(error => { | ||
| cache.set(null); | ||
| return Promise.reject(error); | ||
| }), | ||
| ...arg | ||
| ) | ||
| ); | ||
| } | ||
| }; | ||
| return promiseCacheWrap; | ||
| } | ||
| /** | ||
| * @description 传入 promise 的创建函数, 返回一个包装函数, 额外返回一个取消 promise 的函数 | ||
| * @param {function} fn function: promise | ||
| * @return {function } function: [promise, cancel: function] | ||
| */ | ||
| export function promiseCancel (fn) { | ||
| return function promiseCancelWrap () { | ||
| let cancelReject; | ||
| const cancelPromise = new Promise((resolve, reject) => { | ||
| cancelReject = reject; | ||
| }); | ||
| function cancel (error) { | ||
| return cancelReject(error); | ||
| } | ||
| return [ | ||
| Promise.race([cancelPromise, fn(...arguments)]), | ||
| cancel | ||
| ]; | ||
| }; | ||
| } | ||
| /** | ||
| * @description 传入 promise 的创建函数, 返回一个包装函数, 会自动取消上一个函数中创建的 promise | ||
| * @param {function} fn function: promise | ||
| * @return {function} function: promise | ||
| */ | ||
| export function promiseAutoCancel (fn, error) { | ||
| const wrapFn = promiseCancel(fn); | ||
| let prevCancel = null; | ||
| return function wrapCancel () { | ||
| if (prevCancel) prevCancel(getValue(error)); | ||
| const [promise, cancel] = wrapFn(...arguments); | ||
| prevCancel = cancel; | ||
| return promise; | ||
| }; | ||
| } |
| import { | ||
| promiseCache, | ||
| promiseCancel, | ||
| promiseAutoCancel | ||
| } from './promise.js'; | ||
| import { beforeEach, jest } from '@jest/globals'; | ||
| let promiseCreator; | ||
| let fetch; | ||
| let resolveFaker; | ||
| const param = 'Test'; | ||
| beforeEach(() => { | ||
| const _fetch = jest.fn(); | ||
| const _resolveFaker = jest.fn(); | ||
| fetch = _fetch; | ||
| resolveFaker = _resolveFaker; | ||
| promiseCreator = (param) => new Promise( | ||
| (resolve) => { | ||
| _fetch(param); | ||
| setTimeout( | ||
| () => { | ||
| _resolveFaker(param); | ||
| resolve(param); | ||
| }, | ||
| 20 | ||
| ); | ||
| } | ||
| ); | ||
| }); | ||
| describe('promiseCache 测试', () => { | ||
| let promiseCacheWrap; | ||
| beforeEach(() => { | ||
| promiseCacheWrap = promiseCache(promiseCreator); | ||
| }); | ||
| it('首次调用', async () => { | ||
| expect(fetch).toHaveBeenCalledTimes(0); | ||
| expect(resolveFaker).toHaveBeenCalledTimes(0); | ||
| await expect(promiseCacheWrap(param)).resolves.toBe(param); | ||
| expect(fetch).toHaveBeenCalledTimes(1); | ||
| expect(resolveFaker).toHaveBeenCalledTimes(1); | ||
| }); | ||
| it('获取数据失败', async () => { | ||
| expect(fetch).toHaveBeenCalledTimes(0); | ||
| expect(resolveFaker).toHaveBeenCalledTimes(0); | ||
| let cancel; | ||
| const promiseCancelWrap = promiseCancel(promiseCreator); | ||
| promiseCacheWrap = promiseCache((...arg) => { | ||
| const promiseCancelWrapReturns = promiseCancelWrap(...arg); | ||
| cancel = promiseCancelWrapReturns[1]; | ||
| return promiseCancelWrapReturns[0]; | ||
| }); | ||
| setTimeout(() => { | ||
| cancel('error'); | ||
| }, 10); | ||
| await expect(promiseCacheWrap(param)).rejects.toBe('error'); | ||
| expect(fetch).toHaveBeenCalledTimes(1); | ||
| expect(resolveFaker).toHaveBeenCalledTimes(0); | ||
| }); | ||
| it('读取缓存', async () => { | ||
| expect(fetch).toHaveBeenCalledTimes(0); | ||
| expect(resolveFaker).toHaveBeenCalledTimes(0); | ||
| const first = promiseCacheWrap(param); | ||
| const second = promiseCacheWrap(param); | ||
| await expect(first).resolves.toBe(param); | ||
| await expect(second).resolves.toBe(param); | ||
| expect(first).toBe(second); | ||
| expect(fetch).toHaveBeenCalledTimes(1); | ||
| expect(resolveFaker).toHaveBeenCalledTimes(1); | ||
| const third = promiseCacheWrap(param); | ||
| await expect(third).resolves.toBe(param); | ||
| expect(third).toBe(second); | ||
| expect(fetch).toHaveBeenCalledTimes(1); | ||
| expect(resolveFaker).toHaveBeenCalledTimes(1); | ||
| }); | ||
| it('删除缓存', async () => { | ||
| expect(fetch).toHaveBeenCalledTimes(0); | ||
| expect(resolveFaker).toHaveBeenCalledTimes(0); | ||
| await expect(promiseCacheWrap(param)).resolves.toBe(param); | ||
| expect(fetch).toHaveBeenCalledTimes(1); | ||
| expect(resolveFaker).toHaveBeenCalledTimes(1); | ||
| promiseCacheWrap.cache.set(null); | ||
| await expect(promiseCacheWrap(param)).resolves.toBe(param); | ||
| expect(fetch).toHaveBeenCalledTimes(2); | ||
| expect(resolveFaker).toHaveBeenCalledTimes(2); | ||
| }); | ||
| it('自定义cache', async () => { | ||
| function createCache () { | ||
| let cache = {}; | ||
| return { | ||
| get (id) { return cache[id]; }, | ||
| set (val, id) { | ||
| if (!val && !id) cache = {}; | ||
| else cache[id] = val; | ||
| return val; | ||
| } | ||
| }; | ||
| } | ||
| promiseCacheWrap = promiseCache(promiseCreator, createCache()); | ||
| await expect(promiseCacheWrap(1)).resolves.toBe(1); | ||
| expect(fetch).toHaveBeenCalledTimes(1); | ||
| expect(resolveFaker).toHaveBeenCalledTimes(1); | ||
| await expect(promiseCacheWrap(2)).resolves.toBe(2); | ||
| expect(fetch).toHaveBeenCalledTimes(2); | ||
| expect(resolveFaker).toHaveBeenCalledTimes(2); | ||
| await expect(promiseCacheWrap(1)).resolves.toBe(1); | ||
| expect(fetch).toHaveBeenCalledTimes(2); | ||
| expect(resolveFaker).toHaveBeenCalledTimes(2); | ||
| promiseCacheWrap.cache.set(null, 1); | ||
| await expect(promiseCacheWrap(1)).resolves.toBe(1); | ||
| expect(fetch).toHaveBeenCalledTimes(3); | ||
| expect(resolveFaker).toHaveBeenCalledTimes(3); | ||
| }); | ||
| }); | ||
| describe('promiseCancel 测试', () => { | ||
| let promiseCreatorWrap, | ||
| promiseCreatorWrapReturns; | ||
| beforeEach(() => { | ||
| promiseCreatorWrap = promiseCancel(promiseCreator); | ||
| promiseCreatorWrapReturns = promiseCreatorWrap(param); | ||
| }); | ||
| it('return 数据结构', () => { | ||
| expect(fetch).toHaveBeenCalledTimes(1); | ||
| expect(promiseCreatorWrap).toBeInstanceOf(Function); | ||
| expect(promiseCreatorWrapReturns).toBeInstanceOf(Array); | ||
| expect(promiseCreatorWrapReturns[0]).toBeInstanceOf(Promise); | ||
| expect(promiseCreatorWrapReturns[1]).toBeInstanceOf(Function); | ||
| }); | ||
| it('取消', async () => { | ||
| const error = 'Cancel'; | ||
| setTimeout(() => { | ||
| promiseCreatorWrapReturns[1](error); | ||
| }, 10); | ||
| await expect(promiseCreatorWrapReturns[0]).rejects.toBe(error); | ||
| expect(resolveFaker).not.toHaveBeenCalled(); | ||
| }); | ||
| it('不取消', async () => { | ||
| await expect(promiseCreatorWrapReturns[0]).resolves.toBe(param); | ||
| }); | ||
| }); | ||
| describe('promiseAutoCancel 测试', () => { | ||
| const error = 'Cancel'; | ||
| let promiseAutoCancelWrap; | ||
| beforeEach(() => { | ||
| promiseAutoCancelWrap = promiseAutoCancel(promiseCreator, error); | ||
| }); | ||
| it('调用一次', async () => { | ||
| expect(resolveFaker).toHaveBeenCalledTimes(0); | ||
| expect(fetch).toHaveBeenCalledTimes(0); | ||
| await expect(promiseAutoCancelWrap(param)).resolves.toBe(param); | ||
| expect(resolveFaker).toHaveBeenCalledTimes(1); | ||
| expect(fetch).toHaveBeenCalledTimes(1); | ||
| }); | ||
| it('在首次结束后再次调用', async () => { | ||
| expect(resolveFaker).toHaveBeenCalledTimes(0); | ||
| expect(fetch).toHaveBeenCalledTimes(0); | ||
| await expect(promiseAutoCancelWrap(param)).resolves.toBe(param); | ||
| expect(resolveFaker).toHaveBeenCalledTimes(1); | ||
| expect(fetch).toHaveBeenCalledTimes(1); | ||
| await expect(promiseAutoCancelWrap(param)).resolves.toBe(param); | ||
| expect(resolveFaker).toHaveBeenCalledTimes(2); | ||
| expect(fetch).toHaveBeenCalledTimes(2); | ||
| }); | ||
| it('在首次结束前再次调用', async () => { | ||
| expect(resolveFaker).toHaveBeenCalledTimes(0); | ||
| expect(fetch).toHaveBeenCalledTimes(0); | ||
| const first = promiseAutoCancelWrap(param); | ||
| expect(fetch).toHaveBeenCalledTimes(1); | ||
| const second = promiseAutoCancelWrap(param); | ||
| await expect(first).rejects.toBe(error); | ||
| expect(resolveFaker).toHaveBeenCalledTimes(0); | ||
| expect(fetch).toHaveBeenCalledTimes(2); | ||
| await expect(second).resolves.toBe(param); | ||
| expect(resolveFaker).toHaveBeenCalledTimes(2); | ||
| expect(fetch).toHaveBeenCalledTimes(2); | ||
| }); | ||
| }); |
+31
| /** | ||
| * @description 是否是有长度的数组 | ||
| * @param {any} val any | ||
| * @returns {boolean} boolean | ||
| */ | ||
| export function isValidArray (val) { | ||
| return Array.isArray(val) && val.length !== 0; | ||
| } | ||
| /** | ||
| * @description 如果参数是参数,返回函数的返回值,否则返回参数 | ||
| * @param {any} valOrFn any | ||
| * @returns {any} | ||
| */ | ||
| export function getValue (valOrFn) { | ||
| return typeof valOrFn === 'function' ? valOrFn() : valOrFn; | ||
| } | ||
| /** | ||
| * @description 是否是 promise | ||
| * @param {any} val any | ||
| * @returns {boolean} boolean | ||
| */ | ||
| export function isPromise (val) { | ||
| return ( | ||
| val !== undefined && | ||
| val !== null && | ||
| typeof val.then === 'function' && | ||
| typeof val.catch === 'function' | ||
| ); | ||
| } |
| import { | ||
| isValidArray, | ||
| getValue, | ||
| isPromise | ||
| } from './type.js'; | ||
| describe('isValidArray 测试', () => { | ||
| it('非数组', () => { | ||
| expect(isValidArray(null)).toBe(false); | ||
| expect(isValidArray(undefined)).toBe(false); | ||
| expect(isValidArray(1)).toBe(false); | ||
| expect(isValidArray('')).toBe(false); | ||
| }); | ||
| it('空数组', () => { | ||
| expect(isValidArray([])).toBe(false); | ||
| }); | ||
| it('数组', () => { | ||
| expect(isValidArray([1])).toBe(true); | ||
| expect(isValidArray(new Array(6))).toBe(true); | ||
| }); | ||
| }); | ||
| describe('getValue 测试', () => { | ||
| it('数值', () => { | ||
| expect(getValue(null)).toBe(null); | ||
| expect(getValue(undefined)).toBe(undefined); | ||
| expect(getValue(1)).toBe(1); | ||
| expect(getValue('')).toBe(''); | ||
| }); | ||
| it('函数', () => { | ||
| expect(getValue(() => null)).toBe(null); | ||
| expect(getValue(() => undefined)).toBe(undefined); | ||
| expect(getValue(() => 1)).toBe(1); | ||
| expect(getValue(() => '')).toBe(''); | ||
| }); | ||
| }); | ||
| describe('isPromise 测试', () => { | ||
| it('非 Promise', () => { | ||
| expect(isPromise(null)).toBe(false); | ||
| expect(isPromise(undefined)).toBe(false); | ||
| expect(isPromise(NaN)).toBe(false); | ||
| expect(isPromise(1)).toBe(false); | ||
| expect(isPromise('')).toBe(false); | ||
| }); | ||
| it('类 Promise', () => { | ||
| expect(isPromise({ then: () => {} })).toBe(false); | ||
| expect(isPromise({ catch: () => {} })).toBe(false); | ||
| expect(isPromise({ then: () => {}, catch: () => {} })).toBe(true); | ||
| }); | ||
| it('Promise', () => { | ||
| expect(isPromise(new Promise(() => {}))).toBe(true); | ||
| expect(isPromise(Promise.resolve())).toBe(true); | ||
| }); | ||
| }); |
+17
-10
@@ -1,12 +0,19 @@ | ||
| export default { | ||
| transform: {}, | ||
| collectCoverage: true, | ||
| coverageThreshold: { | ||
| global: { | ||
| branches: 100, | ||
| functions: 100, | ||
| lines: 100, | ||
| statements: 100 | ||
| import path from 'path'; | ||
| export default () => { | ||
| const file = process.argv.find(arg => /\.test\.js$/.test(arg)); | ||
| return { | ||
| rootDir: './', | ||
| transform: {}, | ||
| collectCoverage: true, | ||
| collectCoverageFrom: file ? [path.relative('./', file.replace(/\.test\.js$/, '.js'))] : null, | ||
| coverageThreshold: { | ||
| global: { | ||
| branches: 100, | ||
| functions: 100, | ||
| lines: 100, | ||
| statements: 100 | ||
| } | ||
| } | ||
| } | ||
| }; | ||
| }; |
+27
-4
| { | ||
| "name": "@danep/utils", | ||
| "version": "1.0.0", | ||
| "version": "1.1.0", | ||
| "description": "简单的工具库", | ||
| "type": "module", | ||
| "main": "index.js", | ||
| "main": "./src/index.js", | ||
| "exports": "./src/index.js", | ||
| "scripts": { | ||
| "test": "node --experimental-vm-modules node_modules/.bin/jest" | ||
| "test": "node --experimental-vm-modules node_modules/.bin/jest", | ||
| "eslint": "eslint src/*.js --fix" | ||
| }, | ||
@@ -14,4 +15,26 @@ "author": "autumnLeaves0", | ||
| "devDependencies": { | ||
| "jest": "^26.6.3" | ||
| "eslint": "^7.17.0", | ||
| "eslint-config-standard": "^16.0.2", | ||
| "eslint-plugin-import": "^2.22.1", | ||
| "eslint-plugin-node": "^11.1.0", | ||
| "eslint-plugin-promise": "^4.2.1", | ||
| "husky": "^4.3.7", | ||
| "jest": "^26.6.3", | ||
| "lint-staged": "^10.5.3" | ||
| }, | ||
| "husky": { | ||
| "hooks": { | ||
| "pre-commit": "lint-staged" | ||
| } | ||
| }, | ||
| "lint-staged": { | ||
| "src/**/*.test.js": [ | ||
| "eslint --fix", | ||
| "npm run test" | ||
| ], | ||
| "src/**/*!(*.test).js": [ | ||
| "eslint --fix", | ||
| "npm run test -- --findRelatedTests" | ||
| ] | ||
| } | ||
| } |
+13
-13
@@ -1,16 +0,16 @@ | ||
| export function dateFormat(date, fmt) { | ||
| var o = { | ||
| "Y+": date.getFullYear(), //年 | ||
| "M+": date.getMonth() + 1, //月份 | ||
| "D+": date.getDate(), //日 | ||
| "h+": date.getHours(), //小时 | ||
| "m+": date.getMinutes(), //分 | ||
| "s+": date.getSeconds(), //秒 | ||
| "q+": Math.floor((date.getMonth() + 3) / 3), //季度 | ||
| "x+": date.getMilliseconds() //毫秒 | ||
| export function dateFormat (date, fmt) { | ||
| const o = { | ||
| 'Y+': date.getFullYear(), // 年 | ||
| 'M+': date.getMonth() + 1, // 月份 | ||
| 'D+': date.getDate(), // 日 | ||
| 'h+': date.getHours(), // 小时 | ||
| 'm+': date.getMinutes(), // 分 | ||
| 's+': date.getSeconds(), // 秒 | ||
| 'q+': Math.floor((date.getMonth() + 3) / 3), // 季度 | ||
| 'x+': date.getMilliseconds() // 毫秒 | ||
| }; | ||
| for (var k in o) { | ||
| if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, ("" + o[k]).padStart(RegExp.$1.length, 0)); | ||
| for (const k in o) { | ||
| if (new RegExp('(' + k + ')').test(fmt)) fmt = fmt.replace(RegExp.$1, ('' + o[k]).padStart(RegExp.$1.length, 0)); | ||
| } | ||
| return fmt; | ||
| } | ||
| } |
@@ -5,3 +5,3 @@ import { dateFormat } from './dateFormat.js'; | ||
| it('YYYY年MM月DD日 hh点mm分ss秒xxx 季度q', () => { | ||
| var str = dateFormat(new Date('2000/3/5 12:13:4:800'), 'YYYY年MM月DD日 hh点mm分ss秒xxx 季度q') | ||
| const str = dateFormat(new Date('2000/3/5 12:13:4:800'), 'YYYY年MM月DD日 hh点mm分ss秒xxx 季度q'); | ||
| expect(str).toBe('2000年03月05日 12点13分04秒800 季度1'); | ||
@@ -11,5 +11,5 @@ }); | ||
| it('Y年M月D日 h点m分s秒x ', () => { | ||
| var str = dateFormat(new Date('2000/3/5 12:13:4:800'), 'Y年M月D日 h点m分s秒x') | ||
| const str = dateFormat(new Date('2000/3/5 12:13:4:800'), 'Y年M月D日 h点m分s秒x'); | ||
| expect(str).toBe('2000年3月5日 12点13分4秒800'); | ||
| }); | ||
| }) | ||
| }); |
+4
-2
@@ -1,2 +0,4 @@ | ||
| export { dateFormat } from './dateFormat.js' | ||
| export { urlParse } from './urlParse.js' | ||
| export * from './dateFormat.js'; | ||
| export * from './urlParse.js'; | ||
| export * from './type.js'; | ||
| export * from './promise.js'; |
+8
-7
@@ -1,10 +0,11 @@ | ||
| export function urlParse(url) { | ||
| if (/^https?:\/\/[^?]+\??([^#]*)/.test(url)){ url = RegExp.$1 || '' | ||
| console.log( url, RegExp.$1,RegExp.$2,RegExp.$3)} | ||
| var reg = /([^&=?]+)=?([^&=?]*?)(&|$|#.*)/g | ||
| var o = {} | ||
| export function urlParse (url) { | ||
| if (/^https?:\/\/[^?]+\??([^#]*)/.test(url)) { | ||
| url = RegExp.$1 || ''; | ||
| } | ||
| const reg = /([^&=?]+)=?([^&=?]*?)(&|$|#.*)/g; | ||
| const o = {}; | ||
| while (reg.test(url)) { | ||
| o[RegExp.$1] = decodeURIComponent(RegExp.$2) || 'true' | ||
| o[RegExp.$1] = decodeURIComponent(RegExp.$2) || 'true'; | ||
| } | ||
| return o; | ||
| } | ||
| } |
@@ -5,18 +5,17 @@ import { urlParse } from './urlParse.js'; | ||
| it('普通解析', () => { | ||
| var o = urlParse('www.baidu.com?a=1&b=2&c=3#hash') | ||
| let o = urlParse('www.baidu.com?a=1&b=2&c=3#hash'); | ||
| expect(o).toEqual({ a: '1', b: '2', c: '3' }); | ||
| var o = urlParse('?a=1&b=2&c=3#hash') | ||
| o = urlParse('?a=1&b=2&c=3#hash'); | ||
| expect(o).toEqual({ a: '1', b: '2', c: '3' }); | ||
| var o = urlParse('a=1&b=2&c=3#hash') | ||
| o = urlParse('a=1&b=2&c=3#hash'); | ||
| expect(o).toEqual({ a: '1', b: '2', c: '3' }); | ||
| var o = urlParse('http://www.baidu.com') | ||
| o = urlParse('http://www.baidu.com'); | ||
| expect(o).toEqual({}); | ||
| }); | ||
| it('参数解码', () => { | ||
| var o = urlParse('www.baidu.com?a=%E5%93%88') | ||
| const o = urlParse('www.baidu.com?a=%E5%93%88'); | ||
| expect(o).toEqual({ a: '哈' }); | ||
@@ -26,5 +25,5 @@ }); | ||
| it('空字符串解析成true', () => { | ||
| var o = urlParse('www.baidu.com?a&b=&c=3') | ||
| const o = urlParse('www.baidu.com?a&b=&c=3'); | ||
| expect(o).toEqual({ a: 'true', b: 'true', c: '3' }); | ||
| }); | ||
| }) | ||
| }); |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
15854
312.76%13
62.5%433
546.27%8
700%27
Infinity%