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,65 @@
1
+ import { execSync } from 'node:child_process'
2
+
3
+
4
+ function run(cmd) {
5
+ try {
6
+ return execSync(cmd, { stdio: 'pipe' }).toString().trim()
7
+ } catch {
8
+ return null
9
+ }
10
+ }
11
+
12
+ function fail(msg) {
13
+ console.error(`\n❌ ${ msg }\n`)
14
+ process.exit(1)
15
+ }
16
+
17
+ console.log('\n🔍 npm publish preflight check\n')
18
+
19
+ // 1. registry
20
+ const registry = run('npm config get registry')
21
+ if (registry !== 'https://registry.npmjs.org/') {
22
+ fail(`Invalid registry: ${ registry }`)
23
+ }
24
+ console.log('✅ registry ok')
25
+
26
+ // 2. whoami
27
+ const user = run('npm whoami')
28
+ if (!user) {
29
+ fail('Not logged in or token invalid')
30
+ }
31
+ console.log(`✅ logged in as ${ user }`)
32
+
33
+ // 3. package name
34
+ const pkgName = run('node -p "require(\'../package.json\').name"')
35
+ if (!pkgName) {
36
+ fail('Cannot read package.json name')
37
+ }
38
+ console.log(`📦 package: ${ pkgName }`)
39
+
40
+ // 4. maintainer check (best-effort)
41
+ const maintainers = run(`npm view ${ pkgName } maintainers --json`)
42
+ if (maintainers) {
43
+ try {
44
+ const list = JSON.parse(maintainers)
45
+ const ok = list.some(m => m.name === user)
46
+ if (!ok) {
47
+ fail(`User "${ user }" is not a maintainer of ${ pkgName }`)
48
+ }
49
+ console.log('✅ maintainer check ok')
50
+ } catch {
51
+ console.log('⚠️ cannot parse maintainers, skip')
52
+ }
53
+ } else {
54
+ console.log('⚠️ package not found (first publish?)')
55
+ }
56
+
57
+ // 5. dry run (authoritative)
58
+ console.log('\n🚀 npm publish --dry-run\n')
59
+ try {
60
+ execSync('npm publish --dry-run', { stdio: 'inherit' })
61
+ } catch {
62
+ fail('Dry run failed (likely 2FA / token / permission issue)')
63
+ }
64
+
65
+ console.log('\n🎉 Preflight check passed. Ready to publish.\n')
@@ -0,0 +1,12 @@
1
+ const outer = () => {
2
+ let closures = 0
3
+
4
+ return (): number => {
5
+ return closures++
6
+ }
7
+ }
8
+
9
+ const inner: () => number = outer()
10
+ console.log('>>> inner:', inner())
11
+ console.log('>>> inner:', inner())
12
+
package/_test_/copy.ts ADDED
@@ -0,0 +1,25 @@
1
+ const obj = {
2
+ a: undefined,
3
+ // b: () => {},
4
+ // c: Symbol('sym'),
5
+ d: new Date(),
6
+ e: /abc/,
7
+ f: new Map(),
8
+ g: new Set(),
9
+ h: Infinity,
10
+ i: null,
11
+ }
12
+
13
+ console.log(JSON.stringify(obj))
14
+ // {"d":"2025-06-11T14:03:26.781Z","e":{},"f":{},"g":{},"h":null,"i":null}
15
+
16
+ console.log(JSON.stringify([ ...Object.values(obj) ]))
17
+ // [ null, null, null, '2025-06-11T14:03:26.781Z', {}, {}, {}, null, null ]
18
+
19
+ const arr = [ { a: 1, b: { c: 2 } } ];
20
+ const deep = structuredClone(arr);
21
+
22
+ deep[0].a = 100;
23
+ console.log(arr[0].a);
24
+
25
+ console.log(structuredClone(obj))
@@ -0,0 +1,71 @@
1
+ import { useCallback, useEffect, useRef, useState } from 'react'
2
+
3
+
4
+ function debounce(fn: Function, delay: number) {
5
+ let timer: ReturnType<typeof setTimeout>
6
+
7
+ return (...args: any[]) => {
8
+ timer && clearTimeout(timer)
9
+ timer = setTimeout(() => {
10
+ fn.apply(this, args)
11
+ }, delay)
12
+ }
13
+ }
14
+
15
+
16
+ function throttle(fn: Function, delay: number) {
17
+ let lastCall = 0
18
+
19
+ return (...args: any[]) => {
20
+ let now = Date.now()
21
+ // console.log(now - lastCall, delay)
22
+ if (now - lastCall >= delay) {
23
+ fn.apply(this, args)
24
+ console.log(55)
25
+ lastCall = now
26
+ }
27
+ }
28
+ }
29
+
30
+
31
+ function useDebounce<T>(fn: T, delay = 300) {
32
+ const [ debounced, setDebounced ] = useState(fn)
33
+
34
+ useEffect(() => {
35
+ const timer = setTimeout(() => setDebounced(fn), delay)
36
+ return () => clearTimeout(timer)
37
+ }, [ fn, delay ])
38
+
39
+ return debounced
40
+ }
41
+
42
+
43
+ function useThrottle<T extends (...args: any[]) => any>(fn: T, delay = 300) {
44
+ const lastCall = useRef(0)
45
+
46
+ return useCallback((...args: Parameters<T>) => {
47
+ const now = Date.now()
48
+ if (now - lastCall.current >= delay) {
49
+ lastCall.current = now
50
+ fn(...args)
51
+ }
52
+ }, [fn, delay])
53
+ }
54
+
55
+ const start = Date.now()
56
+
57
+ const log = () => {
58
+ console.log(Date.now() - start, 'aa')
59
+ }
60
+
61
+ // const debounced = debounce(log, 100)
62
+ // const throttled = throttle(log, 10)
63
+ const debounced = useDebounce(log, 100)
64
+ const throttled = throttle(log, 10)
65
+
66
+
67
+ const arr = Array.from(new Array(10 * 1000).keys(), i => i + 1)
68
+
69
+ for (const i of arr) {
70
+ debounced()
71
+ }
@@ -0,0 +1,37 @@
1
+ function Log() {
2
+ return function (
3
+ target: any,
4
+ propertyKey: string,
5
+ descriptor: PropertyDescriptor,
6
+ ) {
7
+ const originalMethod = descriptor.value
8
+
9
+ console.log(target, propertyKey)
10
+ descriptor.value = function (...args: any[]) {
11
+ console.log(`[LOG] ${ propertyKey } called with arguments:`, args)
12
+ return originalMethod.apply(this, args)
13
+ }
14
+
15
+ return descriptor
16
+ }
17
+ }
18
+
19
+ function Test() {
20
+ return function (target: any, propertyKey: string) {
21
+ console.log(target, propertyKey)
22
+ console.log(`[LOG] ${ propertyKey } called with arguments: ${ propertyKey }`)
23
+ }
24
+ }
25
+
26
+ class Example {
27
+ @Test()
28
+ age: number = 18
29
+
30
+ @Log()
31
+ sayHello(name: string, age?: number) {
32
+ console.log(`Hello, ${ name }! Age: ${ age }`)
33
+ }
34
+ }
35
+
36
+ const example = new Example()
37
+ example.sayHello('Alice', 25)
@@ -0,0 +1,35 @@
1
+ const deepCopy = (source: any, map: WeakMap<any, any>) => {
2
+ if (source !== null && typeof source !== 'object') {
3
+ return source
4
+ }
5
+
6
+ if (map.has(source)) {
7
+ return map.get(source)
8
+ }
9
+
10
+ const clone = Array.isArray(source) ? [] : {}
11
+
12
+ map.set(source, clone)
13
+
14
+ for (const key in source) {
15
+ if (source.hasOwnProperty(key)) {
16
+ clone[key] = deepCopy(source[key], map)
17
+ }
18
+ }
19
+
20
+ return clone
21
+ }
22
+
23
+
24
+ const obj = {
25
+ a: 1,
26
+ b: 2,
27
+ date: new Date(),
28
+ map: new Map()
29
+ };
30
+ obj.self = obj;
31
+
32
+
33
+ const symble = Symbol('symbol');
34
+ console.log(structuredClone(obj))
35
+ console.log(JSON.parse(JSON.stringify(obj)));
package/_test_/dom.ts ADDED
@@ -0,0 +1,19 @@
1
+ const getOffsetLeft = (dom: HTMLElement) => {
2
+ let offsetLeft = dom.offsetLeft
3
+ let parent = dom.offsetParent as (HTMLElement | null)
4
+ while (parent) {
5
+ offsetLeft += parent.offsetLeft
6
+ parent = parent.offsetParent as (HTMLElement | null)
7
+ }
8
+ return offsetLeft
9
+ }
10
+
11
+ const getOffsetTop = (dom: HTMLElement) => {
12
+ let offsetTop = dom.offsetTop
13
+ let parent = dom.offsetParent as (HTMLElement | null)
14
+ while (parent) {
15
+ offsetTop += parent.offsetTop
16
+ parent = parent.offsetParent as (HTMLElement | null)
17
+ }
18
+ return offsetTop
19
+ }
@@ -0,0 +1,45 @@
1
+ export const fetchWithTimeout = async <T>(
2
+ url: string,
3
+ options: RequestInit = {},
4
+ timeout: number = 1000,
5
+ type: 'json' | 'text' | 'blob' | 'arrayBuffer' | 'bytes' | 'formData' = 'json',
6
+ ): Promise<T | undefined> => {
7
+ const c = new AbortController()
8
+ const timer = setTimeout(() => c.abort(), timeout)
9
+
10
+ try {
11
+ const res = await fetch(url, { ...options, signal: c.signal })
12
+
13
+ let data: unknown
14
+ switch (type) {
15
+ case 'json':
16
+ data = await res.json()
17
+ break
18
+ case 'text':
19
+ data = await res.text()
20
+ break
21
+ case 'blob':
22
+ data = await res.blob()
23
+ break
24
+ case 'arrayBuffer':
25
+ data = await res.arrayBuffer()
26
+ break
27
+ case 'formData':
28
+ data = await res.formData()
29
+ break
30
+ case 'bytes':
31
+ data = await res.bytes()
32
+ break
33
+ }
34
+
35
+ return data as T
36
+ } catch (e) {
37
+ return void 0
38
+ } finally {
39
+ clearTimeout(timer)
40
+ }
41
+ }
42
+
43
+
44
+ console.log(await fetchWithTimeout('https://example.com', {}, 5000, 'text'))
45
+ export {}
@@ -0,0 +1,48 @@
1
+ import { pkgVersion } from '@peiyanlu/cli-utils'
2
+ import { execSync } from 'node:child_process'
3
+ import { readFileSync } from 'node:fs'
4
+ import { join } from 'node:path'
5
+ import semver, { validRange, valid, satisfies, diff, minVersion, inc, clean } from 'semver'
6
+
7
+
8
+ const pkgPath = join('./package.json')
9
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'))
10
+
11
+ const deps: Record<string, string> = {
12
+ ...pkg.dependencies,
13
+ ...pkg.devDependencies,
14
+ }
15
+
16
+ const changes: string[] = []
17
+
18
+ // for (const [ dep, range ] of Object.entries(deps)) {
19
+ // try {
20
+ // const latest = execSync(`npm view ${ dep } version`).toString().trim()
21
+ // if (validRange(range) && valid(latest) && !satisfies(latest, range)) {
22
+ // const type = diff(minVersion(range)!, latest)
23
+ // if (type && [ 'minor', 'major'].includes(type)) {
24
+ // changes.push(`${ dep }: ${ range } → ${ latest } (${ type })`)
25
+ // }
26
+ // }
27
+ // } catch {}
28
+ // }
29
+ //
30
+ // if (changes.length) {
31
+ // // console.log(`\n📦 Template ${ name }`)
32
+ // changes.forEach(c => console.log(` - ${ c }`))
33
+ // }
34
+
35
+
36
+ // console.log(Object.entries(deps).reduce( (res, [ dep, range ]) => {
37
+ // const latest = execSync(`npm view ${ dep } version`).toString().trim()
38
+ //
39
+ // if (validRange(range) && valid(latest) && !satisfies(latest, range)) {
40
+ // const type = diff(minVersion(range)!, latest)
41
+ // if (type && [ 'minor', 'major' ].includes(type)) {
42
+ // ;(res[type] ??= []).push({ dep, version: latest })
43
+ // }
44
+ // }
45
+ // return res
46
+ // }, {} as Record<string, ({ dep: string, version: string })[]>))
47
+
48
+ console.log(inc(clean('^1.2.3')!, 'minor'))
package/_test_/get.ts ADDED
@@ -0,0 +1,52 @@
1
+ const set = <S extends Record<any, any>, V>(obj: S, keyStr: string, val: V) => {
2
+ let current = obj
3
+
4
+ const keys = keyStr.split('.')
5
+ const last = keys.pop() as string
6
+
7
+ for (const key of keys) {
8
+ if (!(key in current) || typeof current[key] !== 'object') {
9
+ current[key] = {}
10
+ }
11
+ current = current[key]
12
+ }
13
+
14
+ current[last] = val
15
+ }
16
+
17
+
18
+ const obj = {
19
+ foo: {
20
+ bar: {
21
+ dd: '55',
22
+ },
23
+ },
24
+ bb: undefined,
25
+ symbol: Symbol('symbol'),
26
+ reg: /\d+/
27
+ }
28
+ set(obj, 'foo.bar.dd', 'bar')
29
+ console.log(obj)
30
+
31
+
32
+ const arr = [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
33
+ const copied = structuredClone(arr)
34
+ copied[4] = 444
35
+ console.log(arr, copied)
36
+
37
+
38
+ const obj123 = {
39
+ a: undefined,
40
+ b: () => {},
41
+ c: Symbol('c'),
42
+ d: new Date(),
43
+ e: /abc/,
44
+ f: new Map(),
45
+ g: new Set(),
46
+ h: NaN,
47
+ };
48
+ const arr1 = [ new Date(), new Date(), undefined, Symbol('string') ]
49
+ const newObj1 = JSON.parse(JSON.stringify([...Object.values(obj123)]));
50
+ const newObj2 = JSON.parse(JSON.stringify(obj123));
51
+ console.log(newObj1)
52
+ console.log(newObj2)
package/_test_/gh.ts ADDED
@@ -0,0 +1,19 @@
1
+ import gh from 'github-url-to-object';
2
+
3
+
4
+ console.log(gh('git@github.com:peiyanlu/svgoify.git'))
5
+
6
+ console.log(gh('git+git@github.com:peiyanlu/svgoify.git'))
7
+
8
+ console.log(gh('git+https://github.com/vitejs/vite.git'))
9
+
10
+
11
+ gh('github:monkey/business')
12
+ gh('https://github.com/monkey/business')
13
+ gh('https://github.com/monkey/business/tree/master')
14
+ gh('https://github.com/monkey/business/tree/master/nested/file.js')
15
+ gh('https://github.com/monkey/business.git')
16
+ gh('http://github.com/monkey/business')
17
+ gh('git://github.com/monkey/business.git')
18
+ gh('git+https://github.com/monkey/business.git')
19
+ gh('git+git@github.com:peiyanlu/svgoify.git')
@@ -0,0 +1,93 @@
1
+ interface GhResult {
2
+ owner: string;
3
+ repo: string;
4
+ branch?: string;
5
+ }
6
+
7
+ function gh(input: string): GhResult {
8
+ input = input.trim();
9
+
10
+ // 去掉 github: 伪协议
11
+ if (input.startsWith('github:')) {
12
+ input = input.slice('github:'.length);
13
+ }
14
+
15
+ // 处理 git+git@github.com:owner/repo.git 形式
16
+ input = input.replace(/^git\+git@github\.com:/, '');
17
+
18
+ // 处理 git@github.com:owner/repo.git 形式
19
+ input = input.replace(/^git@github\.com:/, '');
20
+
21
+ // 去掉协议+域名
22
+ input = input.replace(/^((git\+)?(https?|git|ssh):\/\/)?(www\.)?github\.com\//, '');
23
+
24
+ // 去掉 .git 后缀
25
+ input = input.replace(/\.git$/, '');
26
+
27
+ // 现在 input 形如: owner/repo[/tree/branch[/...]]
28
+ const parts = input.split('/');
29
+
30
+ if (parts.length < 2) {
31
+ throw new Error('Invalid GitHub repo string');
32
+ }
33
+
34
+ const owner = parts[0];
35
+ const repo = parts[1];
36
+
37
+ // 寻找分支名(branch)
38
+ // 规则:如果第三段是 "tree",第四段即为 branch 名
39
+ let branch: string | undefined;
40
+ if (parts.length >= 4 && parts[2] === 'tree') {
41
+ branch = parts[3];
42
+ }
43
+
44
+ return {
45
+ owner,
46
+ repo,
47
+ branch,
48
+ };
49
+ }
50
+
51
+ console.log(gh('github:monkey/business'))
52
+ console.log(gh('https://github.com/monkey/business'))
53
+ console.log(gh('https://github.com/monkey/business/tree/master'))
54
+ console.log(gh('https://github.com/monkey/business/tree/master/nested/file.js'))
55
+ console.log(gh('https://github.com/monkey/business.git'))
56
+ console.log(gh('http://github.com/monkey/business'))
57
+ console.log(gh('git://github.com/monkey/business.git'))
58
+ console.log(gh('git+https://github.com/monkey/business.git'))
59
+ console.log(gh('git+git@github.com:peiyanlu/svgoify.git'))
60
+
61
+
62
+ console.log(crypto.randomUUID())
63
+ console.log(crypto.randomUUID())
64
+
65
+
66
+ import {
67
+ setInterval,
68
+ } from 'node:timers/promises';
69
+
70
+
71
+ // const inte = 200;
72
+ // console.log(Date.now(),123)
73
+ // for await (const startTime of setInterval(inte, Date.now())) {
74
+ // const now = Date.now();
75
+ // console.log(now);
76
+ // if ((now - startTime) > 1000)
77
+ // break;
78
+ // }
79
+ // console.log(Date.now());
80
+
81
+
82
+ const interval =async (ms: number, count: number, cb: any) => {
83
+ for await (const startTime of setInterval(ms, Date.now())) {
84
+ const now = Date.now();
85
+ cb(now)
86
+ if ((now - startTime) > ms * count)
87
+ break;
88
+ }
89
+ }
90
+
91
+ await interval(200, 5, (n) => {
92
+ console.log(n);
93
+ })
Binary file
Binary file
package/_test_/icon.ts ADDED
@@ -0,0 +1,120 @@
1
+ import path from 'path'
2
+ import fs from 'fs'
3
+ import { execSync } from 'child_process'
4
+ import sharp from 'sharp'
5
+ import pngToIco from 'png-to-ico'
6
+
7
+
8
+ const srcIcon = path.resolve('./icon.png') // 原始透明背景PNG图
9
+
10
+ // 输出目录
11
+ const outDir = path.resolve( './build-icons')
12
+ if (!fs.existsSync(outDir)) fs.mkdirSync(outDir)
13
+
14
+ async function generatePngSizes() {
15
+ const sizes = [ 16, 32, 48, 64, 128, 256, 512, 1024 ]
16
+ for (const size of sizes) {
17
+ await sharp(srcIcon)
18
+ .resize(size, size)
19
+ .toFile(path.join(outDir, `icon_${ size }x${ size }.png`))
20
+ }
21
+ }
22
+
23
+ async function generateIco() {
24
+ // 生成 ico 需要 256, 128, 64, 48, 32, 16 PNG 文件
25
+ const icoSizes = [ 16, 32, 48, 64, 128, 256 ]
26
+ const pngFiles = await Promise.all(
27
+ icoSizes.map((size) =>
28
+ sharp(srcIcon)
29
+ .resize(size, size)
30
+ .png()
31
+ .toBuffer()
32
+ )
33
+ )
34
+ const icoBuffer = await pngToIco(pngFiles)
35
+ fs.writeFileSync(path.join(outDir, 'icon.ico'), icoBuffer)
36
+ }
37
+
38
+ async function generateIcns() {
39
+ // macOS专用,先生成iconset文件夹
40
+ const iconsetDir = path.join(outDir, 'icon.iconset')
41
+ if (!fs.existsSync(iconsetDir)) fs.mkdirSync(iconsetDir)
42
+
43
+ const icnsSizes = [
44
+ 16, 32, 64, 128, 256, 512, 1024
45
+ ]
46
+
47
+ // iconutil需要指定名字和@2x.png
48
+ for (const size of icnsSizes) {
49
+ const filename = `icon_${ size }x${ size }.png`
50
+ const filepath = path.join(iconsetDir, filename)
51
+ await sharp(srcIcon)
52
+ .resize(size, size)
53
+ .toFile(filepath)
54
+
55
+ // 2x图
56
+ const doubleSize = size * 2
57
+ const doubleFilename = `icon_${ size }x${ size }@2x.png`
58
+ const doubleFilepath = path.join(iconsetDir, doubleFilename)
59
+ await sharp(srcIcon)
60
+ .resize(doubleSize, doubleSize)
61
+ .toFile(doubleFilepath)
62
+ }
63
+
64
+ // 执行 iconutil 转换
65
+ try {
66
+ execSync(`iconutil -c icns ${ iconsetDir } -o ${ path.join(outDir, 'icon.icns') }`)
67
+ } catch (e) {
68
+ console.error('iconutil 转换失败,请确认你在 macOS 且已安装 Xcode 命令行工具')
69
+ }
70
+ }
71
+
72
+ // 生成纯白色和纯黑色版本的 tray icon
73
+ async function generateTrayIcons() {
74
+ const trayOutDir = path.join(outDir, 'tray')
75
+ if (!fs.existsSync(trayOutDir)) fs.mkdirSync(trayOutDir)
76
+
77
+ // 纯白色
78
+ await sharp(srcIcon)
79
+ .resize(24, 24)
80
+ .flatten({ background: '#ffffff' })
81
+ .modulate({ brightness: 2 }) // 提亮模拟白色(根据需求调)
82
+ .toFile(path.join(trayOutDir, 'tray_white.png'))
83
+
84
+ // 纯黑色
85
+ await sharp(srcIcon)
86
+ .resize(24, 24)
87
+ .flatten({ background: '#000000' })
88
+ .toFile(path.join(trayOutDir, 'tray_black.png'))
89
+ }
90
+
91
+ // 模糊裁切启动页icon示例
92
+ async function generateSplashIcons() {
93
+ const splashOutDir = path.join(outDir, 'splash')
94
+ if (!fs.existsSync(splashOutDir)) fs.mkdirSync(splashOutDir)
95
+
96
+ const splashSizes = [ 100, 200 ]
97
+
98
+ for (const size of splashSizes) {
99
+ await sharp(srcIcon)
100
+ .resize(size, size)
101
+ .blur(10) // 模糊效果
102
+ .toFile(path.join(splashOutDir, `splash_${ size }.png`))
103
+ }
104
+ }
105
+
106
+ async function main() {
107
+ await generatePngSizes()
108
+ await generateIco()
109
+
110
+ if (process.platform === 'darwin') {
111
+ await generateIcns()
112
+ } else {
113
+ console.log('非 macOS 平台,跳过生成 icns')
114
+ }
115
+
116
+ await generateTrayIcons()
117
+ await generateSplashIcons()
118
+ }
119
+
120
+ main().catch(console.error)
@@ -0,0 +1,30 @@
1
+ import { readFileSync } from 'fs'
2
+
3
+
4
+ function parseIcoSizes(buffer: Buffer): { width: number, height: number }[] {
5
+ const sizes: { width: number, height: number }[] = []
6
+ if (buffer.readUInt16LE(0) !== 0 || buffer.readUInt16LE(2) !== 1) {
7
+ throw new Error('Not a valid ICO file')
8
+ }
9
+ const count = buffer.readUInt16LE(4)
10
+ for (let i = 0; i < count; i++) {
11
+ const entryOffset = 6 + i * 16
12
+ const width = buffer.readUInt8(entryOffset) || 256
13
+ const height = buffer.readUInt8(entryOffset + 1) || 256
14
+ sizes.push({ width, height })
15
+ }
16
+ return sizes
17
+ }
18
+
19
+ const icoPath = './icon.ico' // 改成你的 .ico 路径
20
+
21
+ try {
22
+ const buffer = readFileSync(icoPath)
23
+ const sizes = parseIcoSizes(buffer)
24
+ console.log(`ICO 文件包含 ${ sizes.length } 个图标尺寸:`)
25
+ sizes.forEach(({ width, height }, i) => {
26
+ console.log(` 图标 #${ i + 1 }: ${ width }x${ height }`)
27
+ })
28
+ } catch (error) {
29
+ console.error('读取或解析 .ico 文件失败:', error)
30
+ }