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.
- package/.idea/dictionaries/project.xml +7 -0
- package/.idea/inspectionProfiles/Project_Default.xml +22 -0
- package/.idea/jsLibraryMappings.xml +6 -0
- package/.idea/misc.xml +10 -0
- package/.idea/modules.xml +8 -0
- package/.idea/ts-run-test.iml +12 -0
- package/.idea/vcs.xml +6 -0
- package/.release-it.json +23 -0
- package/CHANGELOG.md +6 -0
- package/_test_/EventBus.ts +33 -0
- package/_test_/EventLoop.js +60 -0
- package/_test_/EventLoop.ts +85 -0
- package/_test_/IO.ts +62 -0
- package/_test_/ShengBei.html +84 -0
- package/_test_/__dirname.ts +17 -0
- package/_test_/aaaa.cjs +4 -0
- package/_test_/array.ts +26 -0
- package/_test_/async.ts +58 -0
- package/_test_/bfc.html +28 -0
- package/_test_/bin.ts +26 -0
- package/_test_/buffer.ts +21 -0
- package/_test_/build-icons/icon.ico +0 -0
- package/_test_/build-icons/icon_1024x1024.png +0 -0
- package/_test_/build-icons/icon_128x128.png +0 -0
- package/_test_/build-icons/icon_16x16.png +0 -0
- package/_test_/build-icons/icon_256x256.png +0 -0
- package/_test_/build-icons/icon_32x32.png +0 -0
- package/_test_/build-icons/icon_48x48.png +0 -0
- package/_test_/build-icons/icon_512x512.png +0 -0
- package/_test_/build-icons/icon_64x64.png +0 -0
- package/_test_/build-icons/splash/splash_100.png +0 -0
- package/_test_/build-icons/splash/splash_200.png +0 -0
- package/_test_/build-icons/tray/tray_black.png +0 -0
- package/_test_/build-icons/tray/tray_white.png +0 -0
- package/_test_/check.ts +65 -0
- package/_test_/closures.ts +12 -0
- package/_test_/copy.ts +25 -0
- package/_test_/debounce.ts +71 -0
- package/_test_/decorator.ts +37 -0
- package/_test_/deepCopy.ts +35 -0
- package/_test_/dom.ts +19 -0
- package/_test_/fetch.ts +45 -0
- package/_test_/get-template-version.ts +48 -0
- package/_test_/get.ts +52 -0
- package/_test_/gh.ts +19 -0
- package/_test_/gh_test.ts +93 -0
- package/_test_/icon.ico +0 -0
- package/_test_/icon.png +0 -0
- package/_test_/icon.ts +120 -0
- package/_test_/iconsize.ts +30 -0
- package/_test_/iterator.ts +112 -0
- package/_test_/link.ts +10 -0
- package/_test_/my/instanceof.ts +35 -0
- package/_test_/my/new.ts +22 -0
- package/_test_/new.ts +6 -0
- package/_test_/object.ts +2 -0
- package/_test_/observer.html +40 -0
- package/_test_/package.json +49 -0
- package/_test_/prototype.ts +27 -0
- package/_test_/proxy.ts +6 -0
- package/_test_/random.ts +29 -0
- package/_test_/react.ts +0 -0
- package/_test_/reg.ts +1 -0
- package/_test_/register.ts +61 -0
- package/_test_/release.config.cts +11 -0
- package/_test_/result.ts +37 -0
- package/_test_/task.ts +196 -0
- package/_test_/temp.md +8 -0
- package/_test_/tesp.ts +23 -0
- package/_test_/test.ts +59 -0
- package/_test_/typed.ts +55 -0
- package/_test_/worker.html +23 -0
- package/_test_/worker.js +4 -0
- package/_test_/worker.ts +5 -0
- package/_test_/xhr.ts +27 -0
- package/_test_//347/210/254/350/231/253.html +93 -0
- package/cache/00a25bc33880251d135060b213749534.jpg +0 -0
- package/cache/10_1730106978_t_NW.jpg +0 -0
- package/cache/11_1730104692_t_NW.jpg +0 -0
- package/cache/15_1730601804_t_NW.jpg +0 -0
- package/cache/17_1730434147_t_NW.jpg +0 -0
- package/cache/19ca581d7f9bf915d3cfb64299a9d5e0.jpg +0 -0
- package/cache/1ccf0b78ab293de8b2aa984773cf315b.jpg +0 -0
- package/cache/21a7518978f91278e75600b75390654e.jpg +0 -0
- package/cache/26ec1ad41910fa9f6236a98c8165a4f3.jpg +0 -0
- package/cache/2_1730114275_t_NW.jpg +0 -0
- package/cache/2a8542fbba235dda9d40c417a94083b5.jpg +0 -0
- package/cache/38_1730084632_t_NW.jpg +0 -0
- package/cache/45205cfc98b45aba7284124f730d37a4.jpg +0 -0
- package/cache/4_1730111766_t_NW.jpg +0 -0
- package/cache/544139e9bbc38597242c2a3d2ba9ede3.jpg +0 -0
- package/cache/60_1729079556_t_NW.jpg +0 -0
- package/cache/61_1729079251_t_NW.jpg +0 -0
- package/cache/65_1729078235_t_NW.jpg +0 -0
- package/cache/6b553b33cb2bc5423ca0a825a9596d7e.jpg +0 -0
- package/cache/74f7bbcd04af36cd224ebed548ba9f96.jpg +0 -0
- package/cache/762fa502959439d4ab1cc2f124c5fe31.jpg +0 -0
- package/cache/7_1730108771_t_NW.jpg +0 -0
- package/cache/82_1730202406_t_NW.jpg +0 -0
- package/cache/861641b72e0aa40abd4ad87c473100f6.jpg +0 -0
- package/cache/91_1730201579_t_NW.jpg +0 -0
- package/cache/95c1eceb38272ac57be2d1f14af3baa5.jpg +0 -0
- package/cache/99cae3416ed7d4ebce46003c291d5cc2.jpg +0 -0
- package/cache/9cf9878a0e979c6b9965ef415cfb43f1.jpg +0 -0
- package/cache/9e9db2fd267dc561244225efc1a872b3.jpg +0 -0
- package/cache/bc7bde2766d07dadd7fafd80854d6c5c.jpg +0 -0
- package/cache/c18d359f69c6b975604549f56237bfa8.jpg +0 -0
- package/cache/cf8143cd193619f57fc1ae06aed1ffec.jpg +0 -0
- package/cache/f3a5f2b40033da74d857c8dc0244948a.jpg +0 -0
- package/cache/f5d0336a68ec2b35eeb76706b02576bd.jpg +0 -0
- package/dist/index.d.ts +0 -0
- package/dist/index.js +2 -0
- package/dist/ip2region.cjs +11 -0
- package/dist/ip2region.d.cts +1 -0
- package/dist/ip2region.d.ts +1 -0
- package/dist/ip2region.js +20 -0
- package/dist/npm.d.ts +1 -0
- package/dist/npm.js +3 -0
- package/dist/pkg-filed.d.ts +1 -0
- package/dist/pkg-filed.js +18 -0
- package/dist/restore-wechat-images.d.ts +1 -0
- package/dist/restore-wechat-images.js +39 -0
- package/package.json +43 -0
- package/record.md +59 -0
- package/src/copyDir.ts +21 -0
- package/src/example.png +0 -0
- package/src/formatText.ts +83 -0
- package/src/index.ts +1 -0
- package/src/ip2region.cts +13 -0
- package/src/npm.ts +5 -0
- package/src/pkg-filed.ts +30 -0
- package/src/puppeteer.ts +51 -0
- package/src/restore-wechat-images.ts +50 -0
- package/src/try.ts +9 -0
- package/src/useCrawler.ts +31 -0
- package/tsconfig.json +20 -0
- package/tsdown.config.ts +15 -0
package/_test_/check.ts
ADDED
|
@@ -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')
|
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
|
+
}
|
package/_test_/fetch.ts
ADDED
|
@@ -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
|
+
})
|
package/_test_/icon.ico
ADDED
|
Binary file
|
package/_test_/icon.png
ADDED
|
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
|
+
}
|