ts-run-test 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. package/.idea/dictionaries/project.xml +7 -0
  2. package/.idea/inspectionProfiles/Project_Default.xml +22 -0
  3. package/.idea/jsLibraryMappings.xml +6 -0
  4. package/.idea/misc.xml +10 -0
  5. package/.idea/modules.xml +8 -0
  6. package/.idea/ts-run-test.iml +12 -0
  7. package/.idea/vcs.xml +6 -0
  8. package/.release-it.json +23 -0
  9. package/CHANGELOG.md +6 -0
  10. package/_test_/EventBus.ts +33 -0
  11. package/_test_/EventLoop.js +60 -0
  12. package/_test_/EventLoop.ts +85 -0
  13. package/_test_/IO.ts +62 -0
  14. package/_test_/ShengBei.html +84 -0
  15. package/_test_/__dirname.ts +17 -0
  16. package/_test_/aaaa.cjs +4 -0
  17. package/_test_/array.ts +26 -0
  18. package/_test_/async.ts +58 -0
  19. package/_test_/bfc.html +28 -0
  20. package/_test_/bin.ts +26 -0
  21. package/_test_/buffer.ts +21 -0
  22. package/_test_/build-icons/icon.ico +0 -0
  23. package/_test_/build-icons/icon_1024x1024.png +0 -0
  24. package/_test_/build-icons/icon_128x128.png +0 -0
  25. package/_test_/build-icons/icon_16x16.png +0 -0
  26. package/_test_/build-icons/icon_256x256.png +0 -0
  27. package/_test_/build-icons/icon_32x32.png +0 -0
  28. package/_test_/build-icons/icon_48x48.png +0 -0
  29. package/_test_/build-icons/icon_512x512.png +0 -0
  30. package/_test_/build-icons/icon_64x64.png +0 -0
  31. package/_test_/build-icons/splash/splash_100.png +0 -0
  32. package/_test_/build-icons/splash/splash_200.png +0 -0
  33. package/_test_/build-icons/tray/tray_black.png +0 -0
  34. package/_test_/build-icons/tray/tray_white.png +0 -0
  35. package/_test_/check.ts +65 -0
  36. package/_test_/closures.ts +12 -0
  37. package/_test_/copy.ts +25 -0
  38. package/_test_/debounce.ts +71 -0
  39. package/_test_/decorator.ts +37 -0
  40. package/_test_/deepCopy.ts +35 -0
  41. package/_test_/dom.ts +19 -0
  42. package/_test_/fetch.ts +45 -0
  43. package/_test_/get-template-version.ts +48 -0
  44. package/_test_/get.ts +52 -0
  45. package/_test_/gh.ts +19 -0
  46. package/_test_/gh_test.ts +93 -0
  47. package/_test_/icon.ico +0 -0
  48. package/_test_/icon.png +0 -0
  49. package/_test_/icon.ts +120 -0
  50. package/_test_/iconsize.ts +30 -0
  51. package/_test_/iterator.ts +112 -0
  52. package/_test_/link.ts +10 -0
  53. package/_test_/my/instanceof.ts +35 -0
  54. package/_test_/my/new.ts +22 -0
  55. package/_test_/new.ts +6 -0
  56. package/_test_/object.ts +2 -0
  57. package/_test_/observer.html +40 -0
  58. package/_test_/package.json +49 -0
  59. package/_test_/prototype.ts +27 -0
  60. package/_test_/proxy.ts +6 -0
  61. package/_test_/random.ts +29 -0
  62. package/_test_/react.ts +0 -0
  63. package/_test_/reg.ts +1 -0
  64. package/_test_/register.ts +61 -0
  65. package/_test_/release.config.cts +11 -0
  66. package/_test_/result.ts +37 -0
  67. package/_test_/task.ts +196 -0
  68. package/_test_/temp.md +8 -0
  69. package/_test_/tesp.ts +23 -0
  70. package/_test_/test.ts +59 -0
  71. package/_test_/typed.ts +55 -0
  72. package/_test_/worker.html +23 -0
  73. package/_test_/worker.js +4 -0
  74. package/_test_/worker.ts +5 -0
  75. package/_test_/xhr.ts +27 -0
  76. package/_test_//347/210/254/350/231/253.html +93 -0
  77. package/cache/00a25bc33880251d135060b213749534.jpg +0 -0
  78. package/cache/10_1730106978_t_NW.jpg +0 -0
  79. package/cache/11_1730104692_t_NW.jpg +0 -0
  80. package/cache/15_1730601804_t_NW.jpg +0 -0
  81. package/cache/17_1730434147_t_NW.jpg +0 -0
  82. package/cache/19ca581d7f9bf915d3cfb64299a9d5e0.jpg +0 -0
  83. package/cache/1ccf0b78ab293de8b2aa984773cf315b.jpg +0 -0
  84. package/cache/21a7518978f91278e75600b75390654e.jpg +0 -0
  85. package/cache/26ec1ad41910fa9f6236a98c8165a4f3.jpg +0 -0
  86. package/cache/2_1730114275_t_NW.jpg +0 -0
  87. package/cache/2a8542fbba235dda9d40c417a94083b5.jpg +0 -0
  88. package/cache/38_1730084632_t_NW.jpg +0 -0
  89. package/cache/45205cfc98b45aba7284124f730d37a4.jpg +0 -0
  90. package/cache/4_1730111766_t_NW.jpg +0 -0
  91. package/cache/544139e9bbc38597242c2a3d2ba9ede3.jpg +0 -0
  92. package/cache/60_1729079556_t_NW.jpg +0 -0
  93. package/cache/61_1729079251_t_NW.jpg +0 -0
  94. package/cache/65_1729078235_t_NW.jpg +0 -0
  95. package/cache/6b553b33cb2bc5423ca0a825a9596d7e.jpg +0 -0
  96. package/cache/74f7bbcd04af36cd224ebed548ba9f96.jpg +0 -0
  97. package/cache/762fa502959439d4ab1cc2f124c5fe31.jpg +0 -0
  98. package/cache/7_1730108771_t_NW.jpg +0 -0
  99. package/cache/82_1730202406_t_NW.jpg +0 -0
  100. package/cache/861641b72e0aa40abd4ad87c473100f6.jpg +0 -0
  101. package/cache/91_1730201579_t_NW.jpg +0 -0
  102. package/cache/95c1eceb38272ac57be2d1f14af3baa5.jpg +0 -0
  103. package/cache/99cae3416ed7d4ebce46003c291d5cc2.jpg +0 -0
  104. package/cache/9cf9878a0e979c6b9965ef415cfb43f1.jpg +0 -0
  105. package/cache/9e9db2fd267dc561244225efc1a872b3.jpg +0 -0
  106. package/cache/bc7bde2766d07dadd7fafd80854d6c5c.jpg +0 -0
  107. package/cache/c18d359f69c6b975604549f56237bfa8.jpg +0 -0
  108. package/cache/cf8143cd193619f57fc1ae06aed1ffec.jpg +0 -0
  109. package/cache/f3a5f2b40033da74d857c8dc0244948a.jpg +0 -0
  110. package/cache/f5d0336a68ec2b35eeb76706b02576bd.jpg +0 -0
  111. package/dist/index.d.ts +0 -0
  112. package/dist/index.js +2 -0
  113. package/dist/ip2region.cjs +11 -0
  114. package/dist/ip2region.d.cts +1 -0
  115. package/dist/ip2region.d.ts +1 -0
  116. package/dist/ip2region.js +20 -0
  117. package/dist/npm.d.ts +1 -0
  118. package/dist/npm.js +3 -0
  119. package/dist/pkg-filed.d.ts +1 -0
  120. package/dist/pkg-filed.js +18 -0
  121. package/dist/restore-wechat-images.d.ts +1 -0
  122. package/dist/restore-wechat-images.js +39 -0
  123. package/package.json +43 -0
  124. package/record.md +59 -0
  125. package/src/copyDir.ts +21 -0
  126. package/src/example.png +0 -0
  127. package/src/formatText.ts +83 -0
  128. package/src/index.ts +1 -0
  129. package/src/ip2region.cts +13 -0
  130. package/src/npm.ts +5 -0
  131. package/src/pkg-filed.ts +30 -0
  132. package/src/puppeteer.ts +51 -0
  133. package/src/restore-wechat-images.ts +50 -0
  134. package/src/try.ts +9 -0
  135. package/src/useCrawler.ts +31 -0
  136. package/tsconfig.json +20 -0
  137. package/tsdown.config.ts +15 -0
@@ -0,0 +1,112 @@
1
+ interface MyIterator {
2
+ data: number[];
3
+
4
+ [Symbol.iterator](): Iterator<number>;
5
+ }
6
+
7
+ const myIterator: MyIterator = {
8
+ data: [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ],
9
+ [Symbol.iterator]() {
10
+ let idx = 0
11
+ const data = this.data
12
+ return {
13
+ next() {
14
+ if (idx < data.length) {
15
+ return { done: false, value: data[idx++] }
16
+ } else {
17
+ return { done: true, value: undefined }
18
+ }
19
+ },
20
+ }
21
+ },
22
+ }
23
+
24
+ for (const input of myIterator) {
25
+ console.log(input)
26
+ }
27
+
28
+ // ------------------------------------------
29
+
30
+ function* myGenerator(): Generator<number> {
31
+ yield 1
32
+ yield 2
33
+ yield 3
34
+ yield 4
35
+ }
36
+
37
+ const gen: Generator<number> = myGenerator()
38
+ for (const number of gen) {
39
+ console.log(number)
40
+ }
41
+
42
+ // ------------------------------------------
43
+
44
+ type IteratorMode = 'entries' | 'keys' | 'values'
45
+
46
+ type ElementType<T, M extends IteratorMode> = M extends 'entries'
47
+ ? [ keyof T, T[keyof T] ]
48
+ : M extends 'keys'
49
+ ? keyof T
50
+ : T[keyof T];
51
+
52
+ type IterableReturn<T, M extends IteratorMode> = T & Iterable<ElementType<T, M>>;
53
+
54
+ const makeIterable = <T extends object, M extends IteratorMode>(obj: T, mode: M): IterableReturn<T, M> => {
55
+ const iterable: Iterable<ElementType<T, M>> =
56
+ mode === 'entries'
57
+ ? Object.entries(obj)
58
+ : mode === 'keys'
59
+ ? Object.keys(obj)
60
+ : Object.values(obj)
61
+
62
+ function* iteratorFactory(): Generator<ElementType<T, M>> {
63
+ for (const item of iterable) {
64
+ yield item
65
+ }
66
+ }
67
+
68
+ return Object.assign(obj, {
69
+ [Symbol.iterator]: iteratorFactory,
70
+ })
71
+ }
72
+
73
+ const makeIterable1 = <T extends object, M extends IteratorMode>(obj: T, mode: M): IterableReturn<T, M> => {
74
+ let temp: ElementType<T, M>[]
75
+ switch (mode) {
76
+ case 'entries':
77
+ temp = Object.entries(obj) as ElementType<T, M>[]
78
+ break
79
+ case 'keys':
80
+ temp = Object.keys(obj) as ElementType<T, M>[]
81
+ break
82
+ case 'values':
83
+ default:
84
+ temp = Object.values(obj) as ElementType<T, M>[]
85
+ break
86
+ }
87
+
88
+ const iteratorFactory = (): Iterator<ElementType<T, M>> => {
89
+ let index = 0
90
+ return {
91
+ next(): IteratorResult<ElementType<T, M>> {
92
+ if (index < temp.length) {
93
+ return { done: false, value: temp[index++] }
94
+ } else {
95
+ return { done: true, value: undefined }
96
+ }
97
+ },
98
+ }
99
+ }
100
+
101
+ return Object.assign(obj, {
102
+ [Symbol.iterator]: iteratorFactory,
103
+ })
104
+ }
105
+
106
+ const make: IterableReturn<{ a: number; b: number; }, 'entries'> = makeIterable({ a: 1, b: 2 }, 'entries')
107
+
108
+ for (const [ key, val ] of make) {
109
+ console.log(key, val)
110
+ }
111
+
112
+ // ------------------------------------------
package/_test_/link.ts ADDED
@@ -0,0 +1,10 @@
1
+ export const pathToLink = (path: string, rootDir = '.', srcDir = '.'): string => path
2
+ .replace(new RegExp(`(^(\/?)${ rootDir }\/)+`, 'g'), '/')
3
+ .replace(new RegExp(`(^(\/?)${ srcDir }\/)+`, 'g'), '/')
4
+ .replace(/\.md$/i, '')
5
+ .replace(/\/index$/g, '/')
6
+ .replace(/\/+/g, '/')
7
+
8
+
9
+ console.log(pathToLink('docs/src/guide/index.md', 'docs', 'src'))
10
+
@@ -0,0 +1,35 @@
1
+ // const instanceOf = <I, C extends object>(inst: I, Constructor: C) => {
2
+ // let proto = Object.getPrototypeOf(inst)
3
+ //
4
+ // while (proto) {
5
+ // if (proto === Constructor.prototype) {
6
+ // return true
7
+ // }
8
+ // proto = Object.getPrototypeOf(proto)
9
+ // }
10
+ //
11
+ // return false
12
+ // }
13
+
14
+ const instanceOf = <T extends object>(
15
+ inst: unknown,
16
+ Constructor: new (...args: any[]) => T,
17
+ ): inst is T => {
18
+ let proto = Object.getPrototypeOf(inst)
19
+
20
+ while (proto) {
21
+ if (proto === Constructor.prototype) {
22
+ return true
23
+ }
24
+ proto = Object.getPrototypeOf(proto)
25
+ }
26
+
27
+ return false
28
+ }
29
+
30
+
31
+ console.log(instanceOf({}, Object))
32
+
33
+ console.log(instanceOf([], Object))
34
+
35
+ console.log(instanceOf({}, String))
@@ -0,0 +1,22 @@
1
+ function myNew<C extends new (...args: any[]) => any>(
2
+ constructor: C,
3
+ ...args: ConstructorParameters<C>
4
+ ): InstanceType<C> {
5
+ const obj = Object.create(constructor.prototype)
6
+
7
+ const result = constructor.apply(obj, args)
8
+
9
+ return (typeof result === 'object' && result !== null) ? result : obj
10
+ }
11
+
12
+
13
+ function myNew1(Func: Function, ...args: any[]) {
14
+ // 1. 创建一个空对象
15
+ const obj = Object.create(null)
16
+ // 2. 将构造函数原型对象指向新对象原型
17
+ obj.__proto__ = Func.prototype
18
+ // 3. 将构造函数的 this 指向新对象
19
+ const result = Func.apply(obj, args)
20
+ // 4. 根据返回值判断
21
+ return result instanceof Object ? result : obj
22
+ }
package/_test_/new.ts ADDED
@@ -0,0 +1,6 @@
1
+ function myNew(Fun: Function, ...args: any[]) {
2
+ const obj = Object.create(null)
3
+ obj._proto_ = Fun.prototype
4
+ const res = Fun.apply(obj, args)
5
+ return res instanceof Object ? res : obj
6
+ }
@@ -0,0 +1,2 @@
1
+ Object.entries({a: 1, b: 2, c: 3})
2
+
@@ -0,0 +1,40 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>Observer</title>
6
+ <style>
7
+ * {
8
+ margin: 0;
9
+ padding: 0;
10
+ }
11
+ </style>
12
+ </head>
13
+
14
+ <body>
15
+ <div class="wrapper">
16
+ <div class="container">
17
+ <h1>Observer</h1>
18
+ <div>55</div>
19
+ </div>
20
+ <!-- /.container -->
21
+ </div>
22
+ </body>
23
+
24
+ <script >
25
+ const observer = new MutationObserver((mutations) => {
26
+ console.log(mutations)
27
+ })
28
+ observer.observe(document.body, {
29
+ childList: true
30
+ })
31
+
32
+
33
+ const resize = new ResizeObserver((entries) => {
34
+ console.log(entries)
35
+ })
36
+ resize.observe(document.body, {
37
+ box: 'border-box'
38
+ })
39
+ </script>
40
+ </html>
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "electron-app",
3
+ "productName": "electron-app",
4
+ "version": "0.0.0",
5
+ "description": "",
6
+ "license": "MIT",
7
+ "main": ".vite/build/main.js",
8
+ "scripts": {
9
+ "dev": "electron-forge start",
10
+ "build": "electron-forge make"
11
+ },
12
+ "devDependencies": {
13
+ "@electron-forge/cli": "^7.10.2",
14
+ "@electron-forge/maker-deb": "^7.10.2",
15
+ "@electron-forge/maker-dmg": "^7.10.2",
16
+ "@electron-forge/maker-msix": "^7.10.2",
17
+ "@electron-forge/maker-rpm": "^7.10.2",
18
+ "@electron-forge/maker-squirrel": "^7.10.2",
19
+ "@electron-forge/maker-zip": "^7.10.2",
20
+ "@electron-forge/plugin-auto-unpack-natives": "^7.10.2",
21
+ "@electron-forge/plugin-electronegativity": "^7.10.2",
22
+ "@electron-forge/plugin-fuses": "^7.10.2",
23
+ "@electron-forge/plugin-vite": "^7.10.2",
24
+ "@electron-forge/publisher-github": "^7.10.2",
25
+ "@electron-forge/shared-types": "^7.10.2",
26
+ "@electron/fuses": "^2.0.0",
27
+ "electron": "^39.2.4",
28
+ "typescript": "^5.9.3",
29
+ "vite": "7.2.6",
30
+ "vite-plugin-html": "^3.2.2"
31
+ },
32
+ "dependencies": {
33
+ "@peiyanlu/electron-ipc": "^1.0.10",
34
+ "@peiyanlu/ts-utils": "^0.0.1"
35
+ },
36
+ "author": {
37
+ "name": "",
38
+ "email": ""
39
+ },
40
+ "os": [
41
+ "win32",
42
+ "darwin",
43
+ "linux"
44
+ ],
45
+ "cpu": [
46
+ "x64",
47
+ "arm64"
48
+ ]
49
+ }
@@ -0,0 +1,27 @@
1
+ class Person {
2
+ protected age: number = 18
3
+
4
+ constructor() {
5
+
6
+ }
7
+
8
+ clone() {
9
+ return cloneInstance(this)
10
+ }
11
+ }
12
+
13
+ export const cloneInstance = <T extends {}>(source: T): T => {
14
+ return Object.assign<T, T>(Object.create(Object.getPrototypeOf(source)), source)
15
+ }
16
+
17
+
18
+ const person = new Person()
19
+
20
+
21
+ console.log(cloneInstance(person), 'constructor')
22
+ console.log(person.clone(), 'constructor')
23
+
24
+ const obj: Record<string, number> = { a: 1, b: 2 }
25
+ const clone = cloneInstance(obj)
26
+ clone.c = 122
27
+ console.log(obj, clone, 'object')
@@ -0,0 +1,6 @@
1
+ const proxy = new Proxy({}, {
2
+ set(target, key, value, receiver) {
3
+ console.log(`set ${ key }=${ value }`);
4
+ return Reflect.set(target, key, value, receiver);
5
+ }
6
+ });
@@ -0,0 +1,29 @@
1
+ // [min, max]
2
+ const random = (min: number, max: number) => {
3
+ return Math.floor(Math.random() * (max - min + 1)) + min
4
+ }
5
+
6
+ // [min, max)
7
+ const random1 = (min: number, max: number) => {
8
+ return Math.floor(Math.random() * (max - min)) + min
9
+ }
10
+
11
+ // (min, max)
12
+ const random2 = (min: number, max: number) => {
13
+ return Math.ceil(Math.random() * (max - min - 1)) + min
14
+ }
15
+
16
+ // (min, max]
17
+ const random3 = (min: number, max: number) => {
18
+ return Math.ceil(Math.random() * (max - min)) + min
19
+ }
20
+
21
+
22
+ for (let i = 0; i < 1000; i++) {
23
+ const r = random2(1, 5)
24
+ // console.log(r)
25
+
26
+ if ([ 1, 5 ].includes(r)) {
27
+ console.log(r)
28
+ }
29
+ }
File without changes
package/_test_/reg.ts ADDED
@@ -0,0 +1 @@
1
+ /src\/index.ts$/.test('')
@@ -0,0 +1,61 @@
1
+ import { spawn } from 'node:child_process'
2
+ import { existsSync } from 'node:fs'
3
+ import { resolve } from 'node:path'
4
+ import { pathToFileURL } from 'node:url'
5
+
6
+
7
+ export function isTSFile(file: string) {
8
+ return /\.(ts|mts|cts)$/.test(file)
9
+ }
10
+
11
+
12
+ export async function loadConfig<T = unknown>(file: string): Promise<T> {
13
+ const url = pathToFileURL(file).href
14
+ console.log(url)
15
+ const mod = await import(url)
16
+ // 兼容 export default / module.exports
17
+ return mod.default ?? mod
18
+ }
19
+
20
+
21
+ export function findConfigFile(cwd = process.cwd()) {
22
+ const files = [
23
+ 'release.config.ts',
24
+ 'release.config.mts',
25
+ 'release.config.cts',
26
+ 'release.config.js',
27
+ 'release.config.mjs',
28
+ 'release.config.cjs',
29
+ ]
30
+
31
+ for (const file of files) {
32
+ const absPath = resolve(cwd, file)
33
+ if (existsSync(absPath)) {
34
+ return absPath
35
+ }
36
+ }
37
+
38
+ return
39
+ }
40
+
41
+ export async function resolveConfig<T = unknown>(cwd?: string): Promise<T | null> {
42
+ const configFile = findConfigFile(cwd)
43
+
44
+ if (!configFile) return null
45
+
46
+ return await loadConfig<T>(configFile)
47
+ }
48
+
49
+ resolveConfig().then(res => {
50
+ console.log(res)
51
+ })
52
+
53
+
54
+ const spawnAsync = (...args: Parameters<typeof spawn>) => {
55
+ return new Promise((resolve) => {
56
+ spawn(...args).on('close', code => {
57
+ return 1 === code ? resolve(true) : resolve(false)
58
+ })
59
+ })
60
+ }
61
+
@@ -0,0 +1,11 @@
1
+ function defineConfig(config) {
2
+ return config
3
+ }
4
+
5
+
6
+ export default defineConfig({
7
+ aaa: 123,
8
+ bbb: 123,
9
+ ccc: 123,
10
+ ddd: 123,
11
+ })
@@ -0,0 +1,37 @@
1
+ console.log([] == ![]) // true
2
+
3
+ console.log(![]) // fase
4
+
5
+
6
+ console.log(1 + 2 + '3')
7
+ console.log(1 + '2' + 3)
8
+
9
+ queueMicrotask(() => {
10
+ console.log('queueMicrotask')
11
+ })
12
+
13
+ setTimeout(()=>{
14
+ console.log('setTimeout')
15
+ })
16
+
17
+ console.log(8888)
18
+
19
+
20
+ console.log(Object.create(null))
21
+
22
+ console.log(({}) + [])
23
+
24
+
25
+ let a = { n: 1 };
26
+ let b = a;
27
+ a.x = a = { n: 2 }
28
+
29
+ console.log(a)
30
+ console.log(b)
31
+
32
+
33
+ console.log([ , , , ].length)
34
+
35
+
36
+ console.log(0 in [ undefined ])
37
+ console.log(0 in [ , ])
package/_test_/task.ts ADDED
@@ -0,0 +1,196 @@
1
+ // ✅ 高级任务池 TaskPool,支持:
2
+ // - 并发控制
3
+ // - 优先级调度
4
+ // - 任务取消(AbortController)
5
+ // - DAG(依赖图)调度
6
+ // - UMD 支持(适配浏览器和 Node)
7
+
8
+ // ------- 类型定义 --------
9
+ type TaskFn<T> = (signal?: AbortSignal) => Promise<T>
10
+
11
+ type TaskState = 'pending' | 'running' | 'completed' | 'failed' | 'canceled'
12
+
13
+ interface TaskNode<T> {
14
+ id: string
15
+ run: TaskFn<T>
16
+ priority: number
17
+ dependencies: string[]
18
+ controller: AbortController
19
+ state: TaskState
20
+ retries: number
21
+ }
22
+
23
+ interface TaskPoolOptions {
24
+ concurrency?: number
25
+ maxRetries?: number
26
+ onProgress?: (done: number, total: number) => void
27
+ onError?: (id: string, error: any) => void
28
+ onComplete?: () => void
29
+ onStart?: (id: string) => void
30
+ onSuccess?: (id: string) => void
31
+ }
32
+
33
+ // ------- TaskPool 实现 --------
34
+ export class TaskPool<T = any> {
35
+ private tasks: Map<string, TaskNode<T>> = new Map()
36
+ private running = 0
37
+ private done = 0
38
+ private readonly concurrency: number
39
+ private readonly maxRetries: number
40
+ private options: TaskPoolOptions
41
+
42
+ constructor(options: TaskPoolOptions = {}) {
43
+ this.concurrency = options.concurrency ?? 2
44
+ this.maxRetries = options.maxRetries ?? 0
45
+ this.options = options
46
+ }
47
+
48
+ addTask(
49
+ id: string,
50
+ taskFn: TaskFn<T>,
51
+ priority = 0,
52
+ dependencies: string[] = [],
53
+ ) {
54
+ const controller = new AbortController()
55
+ const task: TaskNode<T> = {
56
+ id,
57
+ run: taskFn,
58
+ priority,
59
+ dependencies,
60
+ controller,
61
+ state: 'pending',
62
+ retries: 0,
63
+ }
64
+ this.tasks.set(id, task)
65
+ this.schedule()
66
+ }
67
+
68
+ cancelTask(id: string) {
69
+ const task = this.tasks.get(id)
70
+ if (task && task.state === 'pending') {
71
+ task.controller.abort()
72
+ task.state = 'canceled'
73
+ }
74
+ }
75
+
76
+ cancelAll() {
77
+ for (const task of this.tasks.values()) {
78
+ if (task.state === 'pending') {
79
+ task.controller.abort()
80
+ task.state = 'canceled'
81
+ }
82
+ }
83
+ }
84
+
85
+ private getRunnableTasks(): TaskNode<T>[] {
86
+ return [ ...this.tasks.values() ]
87
+ .filter(t =>
88
+ t.state === 'pending' &&
89
+ t.dependencies.every(dep => this.tasks.get(dep)?.state === 'completed'),
90
+ )
91
+ .sort((a, b) => b.priority - a.priority)
92
+ }
93
+
94
+ private schedule() {
95
+ while (this.running < this.concurrency) {
96
+ const task = this.getRunnableTasks()[0]
97
+ if (!task) break
98
+ this.runTask(task).finally()
99
+ }
100
+ }
101
+
102
+ private async runTask(task: TaskNode<T>) {
103
+ task.state = 'running'
104
+ this.running++
105
+ this.options.onStart?.(task.id)
106
+ try {
107
+ await task.run(task.controller.signal)
108
+ task.state = 'completed'
109
+ this.options.onSuccess?.(task.id)
110
+ } catch (e) {
111
+ if (task.controller.signal.aborted) {
112
+ task.state = 'canceled'
113
+ } else if (task.retries < this.maxRetries) {
114
+ task.retries++
115
+ task.state = 'pending'
116
+ this.running--
117
+ return this.schedule()
118
+ } else {
119
+ task.state = 'failed'
120
+ this.options.onError?.(task.id, e)
121
+ }
122
+ } finally {
123
+ this.running--
124
+
125
+ if ([ 'completed', 'failed', 'canceled' ].includes(task.state)) {
126
+ this.done++
127
+ this.options.onProgress?.(this.done, this.tasks.size)
128
+ if (this.done === this.tasks.size) {
129
+ this.options.onComplete?.()
130
+ }
131
+ }
132
+ this.schedule()
133
+ }
134
+ }
135
+ }
136
+
137
+ // ------- 浏览器/Node 兼容(UMD 支持) --------
138
+ // 可用 Rollup/Webpack 打包为 UMD 模块,或如下方式直接挂载:
139
+
140
+ // @ts-ignore
141
+ if (typeof window !== 'undefined') {
142
+ // @ts-ignore
143
+ window.TaskPool = TaskPool
144
+ }
145
+ // @ts-ignore
146
+ if (typeof global !== 'undefined') {
147
+ // @ts-ignore
148
+ global.TaskPool = TaskPool
149
+ }
150
+
151
+
152
+ const sleep = (ms: number, label: string, fail = false) => () =>
153
+ new Promise<string>((resolve, reject) => {
154
+ setTimeout(() => {
155
+ if (fail && Math.random() < 0.5) return reject(new Error(label + ' fail'))
156
+ resolve(label)
157
+ }, ms)
158
+ })
159
+
160
+ const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms))
161
+
162
+ const pool = new TaskPool({
163
+ concurrency: 2,
164
+ maxRetries: 1,
165
+ onStart: id => console.log(`[START] ${ id }`),
166
+ onSuccess: id => console.log(`[SUCCESS] ${ id }`),
167
+ onError: (id, err) => console.warn(`[ERROR] ${ id }:`, err),
168
+ onProgress: (done, total) => console.log(`[PROGRESS] ${ done }/${ total }`),
169
+ onComplete: () => console.log(`[COMPLETE] all tasks finished`),
170
+ })
171
+
172
+ pool.addTask('A', async () => {
173
+ await delay(1000)
174
+ console.log('Task A finished')
175
+ }, 1)
176
+
177
+ pool.addTask('B', async () => {
178
+ await delay(500)
179
+ console.log('Task B finished')
180
+ }, 2)
181
+
182
+ pool.addTask('C', async () => {
183
+ await delay(300)
184
+ console.log('Task C finished')
185
+ }, 1, [ 'A', 'B' ])
186
+
187
+ pool.addTask('D', async signal => {
188
+ await delay(400)
189
+ if (signal?.aborted) throw new Error('Task D was aborted')
190
+ console.log('Task D finished')
191
+ }, 3)
192
+
193
+ // 模拟取消 D
194
+ setTimeout(() => {
195
+ // pool.cancelTask('D')
196
+ }, 100)
package/_test_/temp.md ADDED
@@ -0,0 +1,8 @@
1
+ BFC (块级格式化上下文)是 CSS 渲染过程中的一个独立渲染环境,它规定了内部块级子元素的排列方式,并且隔离外部布局影响
2
+
3
+ 1、独立渲染环境:BFC 内部的元素布局不受外部影响
4
+ 2、布局规则
5
+ 1、内部会计子元素在垂直方向依次排列
6
+ 2、相邻元素的垂直外边距会发生重叠
7
+ 3、子元素的外边距与包含块的左边框相邻
8
+ 3、隔离性:内部子元素的排列方式不会影响外部布局
package/_test_/tesp.ts ADDED
@@ -0,0 +1,23 @@
1
+ import { readFileSync, writeFileSync } from 'node:fs'
2
+ import { join } from 'node:path'
3
+ import { format } from 'node:util'
4
+ import semver from 'semver'
5
+ import minimist from 'minimist'
6
+
7
+
8
+ // const { name, type } = minimist(process.argv.slice(2))
9
+ //
10
+ // const templatePath = join('templates', name, 'template.json')
11
+ //
12
+ // const tpl = JSON.parse(readFileSync(templatePath, 'utf8'))
13
+ // tpl.version = semver.inc(tpl.version, type)!
14
+ //
15
+ // writeFileSync(templatePath, JSON.stringify(tpl, null, 2) + '\n')
16
+ //
17
+ // console.log(`✔ ${ name } bumped to ${ tpl.version }`)
18
+
19
+
20
+ const context = Object.assign({}, { version: '*' });
21
+ const match = format('${version}', context);
22
+
23
+ console.log(match)