-
Notifications
You must be signed in to change notification settings - Fork 22
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
13 changed files
with
3,509 additions
and
777 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 |
---|---|---|
@@ -1,2 +1,4 @@ | ||
/** @type import('haoma').JestConfig */ | ||
module.exports = require('haoma').getJestConfig() | ||
module.exports = require('haoma').getJestConfig({ | ||
transformPackages: ['lodash-es'], | ||
}) |
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
Large diffs are not rendered by default.
Oops, something went wrong.
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,71 @@ | ||
import exec from 'execa' | ||
import fs from 'fs-extra' | ||
import prompts from 'prompts' | ||
import { join } from 'path' | ||
|
||
async function main(rootDir: string) { | ||
const { name: util } = await prompts([ | ||
{ | ||
name: 'name', | ||
type: 'text', | ||
message: '请输入要创建的 util 名称', | ||
}, | ||
]) | ||
|
||
const srcDir = join(rootDir, './src') | ||
const utilsDir = join(srcDir, './utils') | ||
const utilFile = join(utilsDir, `${util}.ts`) | ||
const utilTestFile = join(utilsDir, `${util}.test.ts`) | ||
|
||
const isClass = /^[A-Z]/.test(util) | ||
|
||
console.log('开始写入文件...') | ||
|
||
await Promise.all([ | ||
fs.writeFile( | ||
utilFile, | ||
isClass | ||
? ` | ||
/** | ||
* ${util}。 | ||
*/ | ||
export class ${util} { | ||
} | ||
` | ||
: ` | ||
/** | ||
* ${util}。 | ||
* | ||
* @param value 值 | ||
* @returns 返回结果 | ||
*/ | ||
export function ${util}(value: any): number { | ||
const res = 1 | ||
return res | ||
} | ||
`, | ||
), | ||
fs.writeFile( | ||
utilTestFile, | ||
` | ||
import { ${util} } from './${util}' | ||
describe(${util}.name, () => { | ||
test('ok', () => { | ||
expect(1).toBe(1) | ||
}) | ||
}) | ||
`, | ||
), | ||
]) | ||
|
||
console.log('✔️ 写入文件成功') | ||
|
||
await exec('npm', ['run', 'generate-index'], { | ||
cwd: rootDir, | ||
stdio: 'inherit', | ||
}) | ||
} | ||
|
||
main(join(__dirname, '..')) |
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,10 @@ | ||
import * as lodash from 'lodash-es' | ||
import * as vtils from './index' | ||
|
||
describe('index', () => { | ||
test('应该导出 lodash-es', () => { | ||
expect([...Object.keys(vtils), 'default']).toIncludeAllMembers( | ||
Object.keys(lodash), | ||
) | ||
}) | ||
}) |
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 @@ | ||
export * from 'lodash-es' | ||
|
||
// @index(['./**/*.ts', '!./**/*.test.*'], f => `export * from '${f.path}'`) | ||
export * from './utils/EventBus' | ||
export * from './utils/isPossibleChineseMobilePhoneNumber' | ||
export * from './utils/isUrl' | ||
// @endindex |
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,90 @@ | ||
import { EventBus } from './EventBus' | ||
|
||
type Events = { | ||
enter: () => number | ||
success: () => any | ||
error: (payload: { message: string }) => any | ||
} | ||
|
||
describe(EventBus.name, () => { | ||
test('可订阅发布', () => { | ||
const bus = new EventBus<Events>() | ||
const enterCallback = jest.fn() | ||
bus.on('enter', enterCallback) | ||
bus.emit('enter') | ||
expect(enterCallback).toBeCalled().toBeCalledTimes(1) | ||
}) | ||
|
||
test('可取消订阅', () => { | ||
const bus = new EventBus<Events>() | ||
const enterCallback = jest.fn() | ||
const enterCallback2 = jest.fn() | ||
const enterCallback3 = jest.fn() | ||
bus.on('enter', enterCallback) | ||
const offCallback2 = bus.on('enter', enterCallback2) | ||
bus.on('enter', enterCallback3) | ||
bus.emit('enter') | ||
expect(enterCallback).toBeCalled().toBeCalledTimes(1) | ||
expect(enterCallback2).toBeCalled().toBeCalledTimes(1) | ||
expect(enterCallback3).toBeCalled().toBeCalledTimes(1) | ||
offCallback2() | ||
bus.emit('enter') | ||
expect(enterCallback).toBeCalled().toBeCalledTimes(2) | ||
expect(enterCallback2).toBeCalled().toBeCalledTimes(1) | ||
expect(enterCallback3).toBeCalled().toBeCalledTimes(2) | ||
bus.off('enter', enterCallback3) | ||
bus.emit('enter') | ||
expect(enterCallback).toBeCalled().toBeCalledTimes(3) | ||
expect(enterCallback2).toBeCalled().toBeCalledTimes(1) | ||
expect(enterCallback3).toBeCalled().toBeCalledTimes(2) | ||
bus.off('enter') | ||
bus.emit('enter') | ||
expect(enterCallback).toBeCalled().toBeCalledTimes(3) | ||
expect(enterCallback2).toBeCalled().toBeCalledTimes(1) | ||
expect(enterCallback3).toBeCalled().toBeCalledTimes(2) | ||
}) | ||
|
||
test('可订阅发布多次', () => { | ||
const bus = new EventBus<Events>() | ||
const enterCallback = jest.fn() | ||
const enterCallback2 = jest.fn() | ||
bus.on('enter', enterCallback) | ||
bus.on('enter', enterCallback2) | ||
bus.emit('enter') | ||
bus.emit('enter') | ||
bus.emit('enter') | ||
expect(enterCallback).toBeCalled().toBeCalledTimes(3) | ||
expect(enterCallback2).toBeCalled().toBeCalledTimes(3) | ||
}) | ||
|
||
test('可传递参数', () => { | ||
const bus = new EventBus<Events>() | ||
const errorCallback = jest.fn() | ||
bus.on('error', errorCallback) | ||
bus.emit('error', { message: 'unexpected error' }) | ||
expect(errorCallback).toBeCalled().toBeCalledTimes(1).toBeCalledWith({ | ||
message: 'unexpected error', | ||
}) | ||
}) | ||
|
||
test('可只订阅一次', () => { | ||
const bus = new EventBus<Events>() | ||
const successCallback = jest.fn() | ||
bus.once('success', successCallback) | ||
bus.emit('success') | ||
bus.emit('success') | ||
bus.emit('success') | ||
bus.emit('success') | ||
expect(successCallback).toBeCalled().toBeCalledTimes(1) | ||
}) | ||
|
||
test('可获取订阅回调结果', () => { | ||
const bus = new EventBus<Events>() | ||
const enterCallback = jest.fn().mockImplementation(() => 1) | ||
const enterCallback2 = jest.fn().mockImplementation(() => 2) | ||
bus.on('enter', enterCallback) | ||
bus.on('enter', enterCallback2) | ||
const results = bus.emit('enter') | ||
expect(results).toEqual([1, 2]) | ||
}) | ||
}) |
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,97 @@ | ||
/** | ||
* 事件巴士,管理事件的发布与订阅。 | ||
* | ||
* ``` | ||
* const bus = new EventBus<{ | ||
* success: (payload: { message: string }) => any | ||
* }>() | ||
* bus.on('success', ({ message }) => console.log(message)) | ||
* bus.emit('success', { message: '提交成功' }) | ||
* // => 控制台输出: 提交成功 | ||
* ``` | ||
* | ||
* @template TEvents 事件名称及其对应的回调描述 | ||
*/ | ||
export class EventBus<TEvents extends Record<string, (...args: any[]) => any>> { | ||
/** | ||
* 回调列表。 | ||
*/ | ||
#callbacks: { | ||
[Key in keyof TEvents]: Array<TEvents[Key]> | ||
} = Object.create(null) | ||
|
||
/** | ||
* 订阅事件。 | ||
* | ||
* @param eventName 事件名称 | ||
* @param callback 事件触发回调 | ||
* @returns 返回取消订阅的函数 | ||
*/ | ||
on<TName extends keyof TEvents>( | ||
eventName: TName, | ||
callback: TEvents[TName], | ||
): () => any { | ||
if (!this.#callbacks[eventName]) { | ||
this.#callbacks[eventName] = [] | ||
} | ||
const index = this.#callbacks[eventName].indexOf(callback) | ||
if (index === -1) { | ||
this.#callbacks[eventName].push(callback) | ||
} | ||
return () => this.off(eventName, callback) | ||
} | ||
|
||
/** | ||
* 订阅事件,但只订阅一次即取消订阅。 | ||
* | ||
* @param eventName 事件名称 | ||
* @param callback 事件触发回调 | ||
* @returns 返回取消订阅的函数 | ||
*/ | ||
once<TName extends keyof TEvents>( | ||
eventName: TName, | ||
callback: TEvents[TName], | ||
): () => any { | ||
const off = this.on(eventName, ((...args) => { | ||
off() | ||
callback(...args) | ||
}) as TEvents[TName]) | ||
return off | ||
} | ||
|
||
/** | ||
* 取消订阅事件,若没有指定回调,则取消所有回调。 | ||
* | ||
* @param eventName 事件名称 | ||
* @param callback 事件触发回调 | ||
*/ | ||
off<TName extends keyof TEvents>( | ||
eventName: TName, | ||
callback?: TEvents[TName], | ||
): void { | ||
if (this.#callbacks[eventName] && callback) { | ||
const index = this.#callbacks[eventName].indexOf(callback) | ||
if (index > -1) { | ||
this.#callbacks[eventName].splice(index, 1) | ||
} | ||
} else { | ||
delete this.#callbacks[eventName] | ||
} | ||
} | ||
|
||
/** | ||
* 发布事件。 | ||
* | ||
* @param eventName 事件名称 | ||
* @param args 传给事件回调的参数 | ||
* @returns 返回各事件回调的返回结果组成的数组 | ||
*/ | ||
emit<TName extends keyof TEvents>( | ||
eventName: TName, | ||
...args: Parameters<TEvents[TName]> | ||
): Array<ReturnType<TEvents[TName]>> { | ||
return (this.#callbacks[eventName] || []).map(callback => { | ||
return callback(...args) | ||
}) | ||
} | ||
} |
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,30 @@ | ||
import { isPossibleChineseMobilePhoneNumber } from './isPossibleChineseMobilePhoneNumber' | ||
|
||
describe(isPossibleChineseMobilePhoneNumber.name, () => { | ||
test('不可能是中国的手机号码', () => { | ||
for (const value of [ | ||
'', | ||
110, | ||
120, | ||
10086, | ||
'180800300800', | ||
12345678, | ||
87654321, | ||
]) { | ||
expect(isPossibleChineseMobilePhoneNumber(value)).toBeFalse() | ||
} | ||
}) | ||
|
||
test('可能是中国的手机号码', () => { | ||
for (const value of [ | ||
16080030080, | ||
18087030088, | ||
13907199856, | ||
'13591512420', | ||
19913769406, | ||
18512345657, | ||
]) { | ||
expect(isPossibleChineseMobilePhoneNumber(value)).toBeTrue() | ||
} | ||
}) | ||
}) |
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,14 @@ | ||
/** | ||
* 检测传入的值是否可能是中国的手机号码。 | ||
* | ||
* ``` | ||
* isPossibleChineseMobilePhoneNumber('10086') // => false | ||
* isPossibleChineseMobilePhoneNumber('18087030088') // => true | ||
* ``` | ||
* | ||
* @param value 要检测的值 | ||
* @returns 返回检测结果 | ||
*/ | ||
export function isPossibleChineseMobilePhoneNumber(value: string | number) { | ||
return /^1[3-9][0-9]{9}$/.test(String(value)) | ||
} |
Oops, something went wrong.