sys-shim 0.0.1-6 → 0.0.1-8
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/package.json +15 -3
- package/script/npm-pkg/bin/index.mjs +27 -0
- package/script/npm-pkg/bin/pack.mjs +152 -0
- package/script/npm-pkg/lib/Default.SFX +0 -0
- package/script/npm-pkg/lib/WinRAR.exe +0 -0
- package/script/npm-pkg/shim/win/favicon.ico +0 -0
- package/script/npm-pkg/shim/win/main.exe +0 -0
- package/src/proxy-deep.js +104 -0
- package/src/util.js +386 -0
- package/template/pack/package.json +4 -0
- package/template/pack/preload.js +5 -0
- package/template/pack/readme.md +6 -0
- package/script/bin/sys-shim.cjs +0 -6
package/package.json
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
"name": "sys-shim",
|
3
3
|
"private": false,
|
4
4
|
"type": "module",
|
5
|
-
"version": "0.0.1-
|
5
|
+
"version": "0.0.1-8",
|
6
6
|
"main": "./script/npm-pkg/node/main.min.cjs",
|
7
7
|
"module": "./script/npm-pkg/node/main.min.mjs",
|
8
8
|
"browser": {
|
@@ -10,9 +10,14 @@
|
|
10
10
|
"./script/npm-pkg/node/main.min.mjs": "./script/npm-pkg/browser/main.esm.min.js"
|
11
11
|
},
|
12
12
|
"bin": {
|
13
|
-
"sys-shim": "./script/bin/
|
13
|
+
"sys-shim": "./script/npm-pkg/bin/index.mjs"
|
14
14
|
},
|
15
15
|
"files": [
|
16
|
+
"./src/proxy-deep.js",
|
17
|
+
"./src/util.js",
|
18
|
+
"./template",
|
19
|
+
"./script/npm-pkg/bin",
|
20
|
+
"./script/npm-pkg/lib",
|
16
21
|
"./script/npm-pkg/shim",
|
17
22
|
"./script/npm-pkg/test",
|
18
23
|
"./script/npm-pkg/node/main.min.cjs",
|
@@ -21,6 +26,7 @@
|
|
21
26
|
"./script/npm-pkg/browser/main.esm.min.js"
|
22
27
|
],
|
23
28
|
"scripts": {
|
29
|
+
"sys-shim": "node ./script/npm-pkg/bin/index.mjs",
|
24
30
|
"lint": "eslint ./ --format unix",
|
25
31
|
"lint-fix": "eslint ./ --fix --format unix",
|
26
32
|
"dev": "run-p vite run.shim",
|
@@ -49,9 +55,15 @@
|
|
49
55
|
"proxy-deep": "^3.1.1",
|
50
56
|
"rollup": "^4.17.2",
|
51
57
|
"rpc-websockets": "^7.6.0",
|
52
|
-
"shx": "^0.3.4",
|
53
58
|
"sys-shim": "link:",
|
54
59
|
"vite": "^5.0.0",
|
55
60
|
"vue": "^3.3.8"
|
61
|
+
},
|
62
|
+
"dependencies": {
|
63
|
+
"shx": "^0.3.4",
|
64
|
+
"download": "^8.0.0",
|
65
|
+
"filenamify": "^6.0.0",
|
66
|
+
"minimist": "^1.2.8",
|
67
|
+
"shelljs": "^0.8.5"
|
56
68
|
}
|
57
69
|
}
|
@@ -0,0 +1,27 @@
|
|
1
|
+
#!/usr/bin/env node
|
2
|
+
|
3
|
+
import cp from 'node:child_process'
|
4
|
+
import fs from 'node:fs'
|
5
|
+
import path from 'node:path'
|
6
|
+
import { fileURLToPath } from 'node:url'
|
7
|
+
import process from 'node:process'
|
8
|
+
import minimist from 'minimist'
|
9
|
+
import {fn as pack} from './pack.mjs'
|
10
|
+
|
11
|
+
const __filename = fileURLToPath(import.meta.url)
|
12
|
+
const __dirname = path.dirname(__filename)
|
13
|
+
|
14
|
+
const cwd = `${__dirname}/../shim/win`
|
15
|
+
const argv = minimist(process.argv.slice(2))
|
16
|
+
const argv0 = argv._[0]
|
17
|
+
|
18
|
+
new Promise(async () => {
|
19
|
+
if(argv0 === `pack`) {
|
20
|
+
await pack(argv)
|
21
|
+
} else {
|
22
|
+
cp.execSync(`${cwd}/main.exe`, {
|
23
|
+
cwd,
|
24
|
+
stdio: `inherit`,
|
25
|
+
})
|
26
|
+
}
|
27
|
+
})
|
@@ -0,0 +1,152 @@
|
|
1
|
+
import cp from 'node:child_process'
|
2
|
+
import fs from 'node:fs'
|
3
|
+
import path from 'node:path'
|
4
|
+
import url from 'node:url'
|
5
|
+
import os from 'node:os'
|
6
|
+
import filenamify from 'filenamify'
|
7
|
+
import download from 'download'
|
8
|
+
import shelljs from 'shelljs'
|
9
|
+
import {
|
10
|
+
simpleTemplate,
|
11
|
+
} from '../../../src/util.js'
|
12
|
+
import process from 'node:process'
|
13
|
+
|
14
|
+
const __filename = url.fileURLToPath(import.meta.url)
|
15
|
+
const __dirname = path.dirname(__filename)
|
16
|
+
const pkgDir = path.join(__dirname, `../../../`)
|
17
|
+
|
18
|
+
function determinePathType(filePath) {
|
19
|
+
if((/^(http:\/\/|https:\/\/).+/i).test(filePath)) {
|
20
|
+
return `url`
|
21
|
+
} else if (path.isAbsolute(filePath)) {
|
22
|
+
return `abs`
|
23
|
+
} else if (filePath.startsWith(`./`) || filePath.startsWith(`../`)) {
|
24
|
+
return `relative`
|
25
|
+
} else {
|
26
|
+
return `sys`
|
27
|
+
}
|
28
|
+
}
|
29
|
+
|
30
|
+
|
31
|
+
/**
|
32
|
+
* 获取默认配置
|
33
|
+
*/
|
34
|
+
async function parseArgv(argv) {
|
35
|
+
const cfg = {
|
36
|
+
input: ``,
|
37
|
+
icon: ``,
|
38
|
+
out: ``,
|
39
|
+
unzip: ``,
|
40
|
+
password: ``,
|
41
|
+
...argv,
|
42
|
+
}
|
43
|
+
if(determinePathType(argv.input) === `url`) {
|
44
|
+
cfg.icon = cfg.icon || await getIcon(`${cfg.input}/favicon.ico`)
|
45
|
+
cfg.out = cfg.out || filenamify(cfg.input)
|
46
|
+
} else {
|
47
|
+
cfg.icon = cfg.icon || await getIcon(`${cfg.input}/favicon.ico`)
|
48
|
+
cfg.out = cfg.out || path.parse(cfg.input).name
|
49
|
+
}
|
50
|
+
cfg.unzip = argv.unzip || `sys-shim-app/${cfg.out}`
|
51
|
+
return cfg
|
52
|
+
}
|
53
|
+
|
54
|
+
/**
|
55
|
+
* 如果不存在 icon 则返回默认 icon
|
56
|
+
*/
|
57
|
+
async function getIcon(iconPath) {
|
58
|
+
if(fs.existsSync(iconPath)) {
|
59
|
+
return iconPath
|
60
|
+
} else {
|
61
|
+
try {
|
62
|
+
const res = new url.URL(iconPath)
|
63
|
+
const icon = `${res.protocol}//${res.host}/favicon.ico`
|
64
|
+
const tempIcon = `${os.tmpdir()}/${filenamify(icon)}`
|
65
|
+
fs.writeFileSync(tempIcon, await download(icon))
|
66
|
+
return tempIcon
|
67
|
+
} catch (error) {
|
68
|
+
console.warn(`从 ${iconPath} 获取图标错误,将使用默认图标`, error)
|
69
|
+
return `${pkgDir}/script/npm-pkg/shim/win/favicon.ico`
|
70
|
+
}
|
71
|
+
}
|
72
|
+
}
|
73
|
+
|
74
|
+
function zip(cfg) {
|
75
|
+
const zipBin = `${pkgDir}/script/npm-pkg/lib/WinRAR.exe`
|
76
|
+
const icon = cfg.icon
|
77
|
+
const out = cfg.out
|
78
|
+
const input = cfg.input
|
79
|
+
const unzip = cfg.unzip
|
80
|
+
|
81
|
+
/**
|
82
|
+
* Path 解压后运行
|
83
|
+
* Silent 解压对话框 1 隐藏 2 显示
|
84
|
+
* Overwrite 解压覆盖询问 0 询问 1 覆盖 2 跳过
|
85
|
+
* Update 更新 U 解压不存在的或较新的 F 只更新目标位置已存在的
|
86
|
+
* Shortcut 创建快捷方式
|
87
|
+
* D 桌面创建
|
88
|
+
* P 在开始菜单/程序中创建
|
89
|
+
* T 在启动菜单中创建
|
90
|
+
*/
|
91
|
+
const comment = (() => {
|
92
|
+
const file = `${os.tmpdir()}/comment.txt`
|
93
|
+
const text = `
|
94
|
+
Path=${path.normalize(unzip)}
|
95
|
+
Setup=main
|
96
|
+
Silent=1
|
97
|
+
Overwrite=1
|
98
|
+
Update=U
|
99
|
+
Shortcut=D, main, , "sys-shim app", "${cfg.out}", favicon.ico
|
100
|
+
`.split(`\n`).map(item => item.trim()).join(`\n`).trim()
|
101
|
+
fs.writeFileSync(file, text)
|
102
|
+
return file
|
103
|
+
})()
|
104
|
+
|
105
|
+
/**
|
106
|
+
* a 添加压缩
|
107
|
+
* -r 递归
|
108
|
+
* -ep1 从名称中排除主文件夹
|
109
|
+
* -inul 关闭错误信息
|
110
|
+
* -y 假设全部的询问回应皆为“是”
|
111
|
+
* -sfx 创建自解压文件 默认为 Default.SFX 模块,修改示例 -sfxWinCon.SFX
|
112
|
+
* -iicon 指定自解压图标
|
113
|
+
* -iimg 指定自解压图片
|
114
|
+
* -m 设置压缩等级,-m0 为存储,-m5 为最优,默认 -m3 标准
|
115
|
+
* -p 使用密码,例如 -pMyPassWord
|
116
|
+
* -hp 使用密码,并加密文件名
|
117
|
+
* -z 从文件中读取注释
|
118
|
+
* -ibck 在后台运行 WinRAR
|
119
|
+
* -idv 显示详细输出
|
120
|
+
*/
|
121
|
+
const outV = `${out}-${Date.now()}`
|
122
|
+
const cmd = `${zipBin} a -r -ep1 -y -ibck -sfx -iicon"${icon}" -z"${comment}" "${outV}" ${input}/*`
|
123
|
+
cp.execSync(cmd, {stdio: `inherit`})
|
124
|
+
}
|
125
|
+
|
126
|
+
function genFile(cfg) {
|
127
|
+
const newCfg = {
|
128
|
+
input: `${os.tmpdir()}/pack.temp`,
|
129
|
+
icon: `${os.tmpdir()}/pack.temp/favicon.ico`,
|
130
|
+
}
|
131
|
+
shelljs.rm(`-fr`, newCfg.input)
|
132
|
+
shelljs.cp(`-fr`, `${pkgDir}/template/pack`, newCfg.input)
|
133
|
+
shelljs.cp(`-f`, `${pkgDir}/script/npm-pkg/shim/win/main.exe`, newCfg.input)
|
134
|
+
shelljs.cp(`-f`, `${pkgDir}/script/npm-pkg/shim/win/favicon.ico`, newCfg.input)
|
135
|
+
shelljs.cp(`-f`, cfg.icon, `${newCfg.input}/favicon.ico`)
|
136
|
+
const newStr = simpleTemplate(fs.readFileSync(`${newCfg.input}/package.json`, `utf8`), {
|
137
|
+
page: determinePathType(cfg.input) === `url` ? cfg.input : `page.html`,
|
138
|
+
})
|
139
|
+
fs.writeFileSync(`${newCfg.input}/package.json`, newStr)
|
140
|
+
return newCfg
|
141
|
+
}
|
142
|
+
|
143
|
+
|
144
|
+
export async function fn(argv) {
|
145
|
+
const cfg = await parseArgv(argv)
|
146
|
+
const {input, icon} = genFile(cfg)
|
147
|
+
cfg.input = input
|
148
|
+
cfg.icon = icon
|
149
|
+
console.log(cfg)
|
150
|
+
await zip(cfg)
|
151
|
+
|
152
|
+
}
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,104 @@
|
|
1
|
+
'use strict'
|
2
|
+
|
3
|
+
function parsePath(text) {
|
4
|
+
return text.split(`.`)
|
5
|
+
}
|
6
|
+
|
7
|
+
function push(arr, el) {
|
8
|
+
const newArr = arr.slice()
|
9
|
+
newArr.push(el)
|
10
|
+
return newArr
|
11
|
+
}
|
12
|
+
|
13
|
+
// names of the traps that can be registered with ES6's Proxy object
|
14
|
+
const trapNames = [
|
15
|
+
`apply`,
|
16
|
+
`construct`,
|
17
|
+
`defineProperty`,
|
18
|
+
`deleteProperty`,
|
19
|
+
`enumerate`,
|
20
|
+
`get`,
|
21
|
+
`getOwnPropertyDescriptor`,
|
22
|
+
`getPrototypeOf`,
|
23
|
+
`has`,
|
24
|
+
`isExtensible`,
|
25
|
+
`ownKeys`,
|
26
|
+
`preventExtensions`,
|
27
|
+
`set`,
|
28
|
+
`setPrototypeOf`,
|
29
|
+
]
|
30
|
+
|
31
|
+
// a list of paramer indexes that indicate that the a recieves a key at that parameter
|
32
|
+
// this information will be used to update the path accordingly
|
33
|
+
const keys = {
|
34
|
+
get: 1,
|
35
|
+
set: 1,
|
36
|
+
deleteProperty: 1,
|
37
|
+
has: 1,
|
38
|
+
defineProperty: 1,
|
39
|
+
getOwnPropertyDescriptor: 1,
|
40
|
+
}
|
41
|
+
|
42
|
+
export function DeepProxy(rootTarget, traps, options) {
|
43
|
+
|
44
|
+
let path = []
|
45
|
+
let _userData = {}
|
46
|
+
|
47
|
+
if (options !== undefined && typeof options.path !== `undefined`) {
|
48
|
+
path = parsePath(options.path)
|
49
|
+
}
|
50
|
+
if (options !== undefined && typeof options.userData !== `undefined`) {
|
51
|
+
_userData = options.userData
|
52
|
+
}
|
53
|
+
|
54
|
+
function createProxy(target, path, userData = {}) {
|
55
|
+
|
56
|
+
// avoid creating a new object between two traps
|
57
|
+
const context = { rootTarget, path }
|
58
|
+
Object.assign(context, _userData, userData)
|
59
|
+
|
60
|
+
const realTraps = {}
|
61
|
+
|
62
|
+
for (const trapName of trapNames) {
|
63
|
+
const keyParamIdx = keys[trapName]
|
64
|
+
, trap = traps[trapName]
|
65
|
+
|
66
|
+
if (typeof trap !== `undefined`) {
|
67
|
+
|
68
|
+
if (typeof keyParamIdx !== `undefined`) {
|
69
|
+
|
70
|
+
realTraps[trapName] = function () {
|
71
|
+
|
72
|
+
const key = arguments[keyParamIdx]
|
73
|
+
|
74
|
+
// update context for this trap
|
75
|
+
context.nest = function (nestedTarget, { userData = {} } = {}) {
|
76
|
+
if (nestedTarget === undefined)
|
77
|
+
nestedTarget = rootTarget
|
78
|
+
return createProxy(nestedTarget, push(path, key), userData)
|
79
|
+
}
|
80
|
+
return trap.apply(context, arguments)
|
81
|
+
}
|
82
|
+
} else {
|
83
|
+
|
84
|
+
realTraps[trapName] = function () {
|
85
|
+
|
86
|
+
// update context for this trap
|
87
|
+
context.nest = function (nestedTarget, { userData = {} } = {}) {
|
88
|
+
if (nestedTarget === undefined)
|
89
|
+
nestedTarget = {}
|
90
|
+
return createProxy(nestedTarget, path, userData)
|
91
|
+
}
|
92
|
+
|
93
|
+
return trap.apply(context, arguments)
|
94
|
+
}
|
95
|
+
}
|
96
|
+
}
|
97
|
+
}
|
98
|
+
|
99
|
+
return new Proxy(target, realTraps)
|
100
|
+
}
|
101
|
+
|
102
|
+
return createProxy(rootTarget, path)
|
103
|
+
|
104
|
+
}
|
package/src/util.js
ADDED
@@ -0,0 +1,386 @@
|
|
1
|
+
import { DeepProxy } from './proxy-deep.js'
|
2
|
+
const KEYS_MAP = new Map()
|
3
|
+
export function get([key]) {
|
4
|
+
return KEYS_MAP.get(key) || key
|
5
|
+
}
|
6
|
+
|
7
|
+
/**
|
8
|
+
* 获取 `/**` 中 `*` 号的数量
|
9
|
+
* @param {*} inputStr
|
10
|
+
* @returns
|
11
|
+
*/
|
12
|
+
export function getCodeLine(inputStr) {
|
13
|
+
// 使用正则表达式匹配以 `/` 开头,后面跟着连续的 `*` 号的模式,使用贪婪匹配
|
14
|
+
const pattern = /\/\*{1,}/g
|
15
|
+
const matches = inputStr.match(pattern)
|
16
|
+
|
17
|
+
// 如果没有匹配到任何 '/**',返回0或者其他默认值
|
18
|
+
if (!matches) {
|
19
|
+
return 0
|
20
|
+
}
|
21
|
+
|
22
|
+
// 初始化最大 '**' 数量和对应的 '/**' 字符串
|
23
|
+
let maxAsterisks = 0
|
24
|
+
let maxAsterisksMatch = ``
|
25
|
+
|
26
|
+
// 遍历所有匹配结果,找出包含最多 '*' 号的那一个
|
27
|
+
for (const match of matches) {
|
28
|
+
const asterisksCount = match.length - 1 // 减去前面的 '/'
|
29
|
+
if (asterisksCount > maxAsterisks) {
|
30
|
+
maxAsterisks = asterisksCount
|
31
|
+
maxAsterisksMatch = match
|
32
|
+
}
|
33
|
+
}
|
34
|
+
|
35
|
+
// 返回最多的 '*' 号数量
|
36
|
+
return maxAsterisks
|
37
|
+
}
|
38
|
+
|
39
|
+
export function deepProxy({
|
40
|
+
keys = [`then`, `catch`],
|
41
|
+
cb = (records) => {
|
42
|
+
return new Promise(async (res, rej) => {
|
43
|
+
// 模拟异步操作
|
44
|
+
setTimeout(() => {
|
45
|
+
res(records)
|
46
|
+
}, Math.random() * 1000)
|
47
|
+
})
|
48
|
+
},
|
49
|
+
} = {}) {
|
50
|
+
keys.forEach(key => {
|
51
|
+
let _val = Symbol(key)
|
52
|
+
KEYS_MAP.set(key, _val)
|
53
|
+
KEYS_MAP.set(_val, key)
|
54
|
+
})
|
55
|
+
function getRecords(context = {}) {
|
56
|
+
return context.records || []
|
57
|
+
}
|
58
|
+
// 代理处理程序
|
59
|
+
const handler = {
|
60
|
+
get(target, key, receiver) {
|
61
|
+
let records = getRecords(this)
|
62
|
+
if (keys.includes(key)) {
|
63
|
+
let promise = cb(records)
|
64
|
+
records.hackRun = true // 已运行过
|
65
|
+
return promise[key].bind(promise)
|
66
|
+
} else {
|
67
|
+
records.push({ type: `get`, key: get([key]) })
|
68
|
+
let newTarget = function () { }
|
69
|
+
return this.nest(newTarget, { userData: { records } })
|
70
|
+
}
|
71
|
+
},
|
72
|
+
apply(target, thisArg, args) {
|
73
|
+
let records = getRecords(this)
|
74
|
+
setTimeout(() => {
|
75
|
+
let recordsEnd = getRecords(this)
|
76
|
+
!recordsEnd.hackRun && cb(recordsEnd)
|
77
|
+
}, 0)
|
78
|
+
const key = records[records.length - 1].key
|
79
|
+
records[records.length - 1] = { type: `apply`, key, arg: args }
|
80
|
+
let newTarget = function () { }
|
81
|
+
return this.nest(newTarget, { userData: { records } })
|
82
|
+
},
|
83
|
+
construct(target, args) {
|
84
|
+
let records = getRecords(this)
|
85
|
+
records.push({ type: `construct`, arg: args })
|
86
|
+
let newTarget = function () { }
|
87
|
+
return this.nest(newTarget, { userData: { records } })
|
88
|
+
},
|
89
|
+
defineProperty(target, key, args) {
|
90
|
+
let records = getRecords(this)
|
91
|
+
records.push({ type: `defineProperty`, key, arg: args })
|
92
|
+
let newTarget = function () { }
|
93
|
+
return this.nest(newTarget, { userData: { records } })
|
94
|
+
},
|
95
|
+
deleteProperty(target, key) {
|
96
|
+
let records = getRecords(this)
|
97
|
+
records.push({ type: `deleteProperty`, key })
|
98
|
+
let newTarget = function () { }
|
99
|
+
return this.nest(newTarget, { userData: { records } })
|
100
|
+
|
101
|
+
},
|
102
|
+
set(target, key, value) {
|
103
|
+
let records = getRecords(this)
|
104
|
+
records.push({ type: `set`, key, arg: value })
|
105
|
+
let newTarget = function () { }
|
106
|
+
return this.nest(newTarget, { userData: { records } })
|
107
|
+
},
|
108
|
+
getOwnPropertyDescriptor(target, prop) {
|
109
|
+
let records = getRecords(this)
|
110
|
+
records.push({ type: `getOwnPropertyDescriptor`, key: prop })
|
111
|
+
let newTarget = function () { }
|
112
|
+
return { configurable: true, enumerable: true, value: this.nest(newTarget) }
|
113
|
+
},
|
114
|
+
getPrototypeOf(target) {
|
115
|
+
let records = getRecords(this)
|
116
|
+
records.push({ type: `getPrototypeOf` })
|
117
|
+
let newTarget = function () { }
|
118
|
+
return this.nest(newTarget, { userData: { records } })
|
119
|
+
},
|
120
|
+
has(target, prop) {
|
121
|
+
let records = getRecords(this)
|
122
|
+
records.push({ type: `has`, key: prop })
|
123
|
+
return true
|
124
|
+
},
|
125
|
+
isExtensible(target) {
|
126
|
+
let records = getRecords(this)
|
127
|
+
records.push({ type: `isExtensible` })
|
128
|
+
return true
|
129
|
+
},
|
130
|
+
setPrototypeOf(target, prototype) {
|
131
|
+
let records = getRecords(this)
|
132
|
+
records.push({ type: `setPrototypeOf`, arg: prototype })
|
133
|
+
return true
|
134
|
+
},
|
135
|
+
ownKeys(target) {
|
136
|
+
let records = getRecords(this)
|
137
|
+
records.push({ type: `ownKeys` })
|
138
|
+
return Reflect.ownKeys(target)
|
139
|
+
},
|
140
|
+
preventExtensions(target) {
|
141
|
+
let records = getRecords(this)
|
142
|
+
records.push({ type: `preventExtensions` })
|
143
|
+
Object.preventExtensions(target)
|
144
|
+
return true
|
145
|
+
},
|
146
|
+
}
|
147
|
+
|
148
|
+
// 返回初始对象的代理
|
149
|
+
return new DeepProxy({}, handler)
|
150
|
+
}
|
151
|
+
|
152
|
+
export function binaryArrayToBuffer(binaryArray) {
|
153
|
+
let buffer = new ArrayBuffer(binaryArray.length)
|
154
|
+
let view = new Uint8Array(buffer)
|
155
|
+
for (let i = 0; i < binaryArray.length; i++) {
|
156
|
+
view[i] = binaryArray[i]
|
157
|
+
}
|
158
|
+
return buffer
|
159
|
+
}
|
160
|
+
|
161
|
+
/**
|
162
|
+
* 删除左边空格
|
163
|
+
* @param {*} str
|
164
|
+
* @returns
|
165
|
+
*/
|
166
|
+
export function removeLeft(str) {
|
167
|
+
const lines = str.split(`\n`)
|
168
|
+
// 获取应该删除的空白符数量
|
169
|
+
const minSpaceNum = lines.filter(item => item.trim())
|
170
|
+
.map(item => item.match(/(^\s+)?/)[0].length)
|
171
|
+
.sort((a, b) => a - b)[0]
|
172
|
+
// 删除空白符
|
173
|
+
const newStr = lines
|
174
|
+
.map(item => item.slice(minSpaceNum))
|
175
|
+
.join(`\n`)
|
176
|
+
return newStr
|
177
|
+
}
|
178
|
+
|
179
|
+
/**
|
180
|
+
* 简单的模板功能
|
181
|
+
* @param {*} template
|
182
|
+
* @param {*} data
|
183
|
+
* @returns
|
184
|
+
*/
|
185
|
+
export function simpleTemplate(template, data) {
|
186
|
+
return template.replace(/#\{(\w+)\}/g, (match, key, pos) => {
|
187
|
+
const lineStr = getLineContainingChar(template, pos)
|
188
|
+
const spaceNum = getMinSpaceNum(lineStr)
|
189
|
+
const val = data[key] || ``
|
190
|
+
const newVal = addSpace(val, {num: spaceNum})
|
191
|
+
return newVal
|
192
|
+
})
|
193
|
+
}
|
194
|
+
|
195
|
+
/**
|
196
|
+
* 返回字符所在位置的整行文本
|
197
|
+
* @param {*} text
|
198
|
+
* @param {*} charPosition
|
199
|
+
* @returns
|
200
|
+
*/
|
201
|
+
export function getLineContainingChar(text, charPosition) {
|
202
|
+
// 将多行文本分割成行数组
|
203
|
+
const lines = text.split(`\n`)
|
204
|
+
|
205
|
+
// 遍历行数组,找到包含给定字符位置的行
|
206
|
+
for (let i = 0; i < lines.length; i++) {
|
207
|
+
// 计算当前行的字符位置
|
208
|
+
const lineLength = lines[i].length
|
209
|
+
if (charPosition < lineLength) {
|
210
|
+
// 如果字符位置在当前行内,返回该行
|
211
|
+
return lines[i]
|
212
|
+
} else {
|
213
|
+
// 否则,减去当前行的长度,继续检查下一行
|
214
|
+
charPosition -= lineLength + 1 // +1 是为了减去换行符
|
215
|
+
}
|
216
|
+
}
|
217
|
+
|
218
|
+
// 如果没有找到包含给定字符位置的行,返回null或适当的错误信息
|
219
|
+
return null
|
220
|
+
}
|
221
|
+
|
222
|
+
/**
|
223
|
+
* 获取最小空白符数量
|
224
|
+
* @param {*} str
|
225
|
+
* @returns
|
226
|
+
*/
|
227
|
+
export function getMinSpaceNum(str) {
|
228
|
+
const lines = str.split(`\n`)
|
229
|
+
const minSpaceNum = lines.filter(item => item.trim())
|
230
|
+
.map(item => item.match(/(^\s+)?/)[0].length)
|
231
|
+
.sort((a, b) => a - b)[0]
|
232
|
+
return minSpaceNum
|
233
|
+
}
|
234
|
+
|
235
|
+
/**
|
236
|
+
* 添加空白符
|
237
|
+
* @param {*} str
|
238
|
+
* @param {*} opt
|
239
|
+
* @returns
|
240
|
+
*/
|
241
|
+
export function addSpace(str, opt = {}) {
|
242
|
+
opt = {
|
243
|
+
num: 0, // 字符符数量
|
244
|
+
space: ` `,
|
245
|
+
skip: 0, // 要跳过设置的索引
|
246
|
+
...opt,
|
247
|
+
}
|
248
|
+
const lines = str.split(`\n`)
|
249
|
+
const newStr = lines
|
250
|
+
.map((item, index) => index <= opt.skip ? item : opt.space.repeat(opt.num) + item)
|
251
|
+
.join(`\n`)
|
252
|
+
return newStr
|
253
|
+
}
|
254
|
+
|
255
|
+
/**
|
256
|
+
* 获取 uuid
|
257
|
+
* @returns
|
258
|
+
*/
|
259
|
+
export function getUuid () {
|
260
|
+
if (typeof crypto === `object`) {
|
261
|
+
if (typeof crypto.randomUUID === `function`) {
|
262
|
+
return crypto.randomUUID()
|
263
|
+
}
|
264
|
+
if (typeof crypto.getRandomValues === `function` && typeof Uint8Array === `function`) {
|
265
|
+
const callback = (c) => {
|
266
|
+
const num = Number(c)
|
267
|
+
return (num ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (num / 4)))).toString(16)
|
268
|
+
}
|
269
|
+
return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, callback)
|
270
|
+
}
|
271
|
+
}
|
272
|
+
let timestamp = new Date().getTime()
|
273
|
+
let perforNow = (typeof performance !== `undefined` && performance.now && performance.now() * 1000) || 0
|
274
|
+
return `xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx`.replace(/[xy]/g, (c) => {
|
275
|
+
let random = Math.random() * 16
|
276
|
+
if (timestamp > 0) {
|
277
|
+
random = (timestamp + random) % 16 | 0
|
278
|
+
timestamp = Math.floor(timestamp / 16)
|
279
|
+
} else {
|
280
|
+
random = (perforNow + random) % 16 | 0
|
281
|
+
perforNow = Math.floor(perforNow / 16)
|
282
|
+
}
|
283
|
+
return (c === `x` ? random : (random & 0x3) | 0x8).toString(16)
|
284
|
+
})
|
285
|
+
}
|
286
|
+
|
287
|
+
export function isUTF8MultiByteStart(byte) {
|
288
|
+
// 如果字节的高位为11,则是多字节字符的起始字节
|
289
|
+
return (byte & 0xC0) === 0xC0
|
290
|
+
}
|
291
|
+
|
292
|
+
export function isUTF8MultiByteContinuation(byte) {
|
293
|
+
// 如果字节的高位为10,则是多字节字符的延续字节
|
294
|
+
return (byte & 0xC0) === 0x80
|
295
|
+
}
|
296
|
+
|
297
|
+
|
298
|
+
/**
|
299
|
+
* 根据字节长度分割字符串
|
300
|
+
* @param {*} param0
|
301
|
+
* @returns
|
302
|
+
*/
|
303
|
+
export function sliceStringByBytes({lib, str, sliceLength}) {
|
304
|
+
const uint8Array = lib.encoder.encode(str)
|
305
|
+
let slices = []
|
306
|
+
let start = 0
|
307
|
+
|
308
|
+
while (start < uint8Array.length) {
|
309
|
+
let end = start + sliceLength
|
310
|
+
if (end > uint8Array.length) {
|
311
|
+
end = uint8Array.length
|
312
|
+
} else {
|
313
|
+
// 确保不在多字节字符中间断开
|
314
|
+
while (end > start && isUTF8MultiByteContinuation(uint8Array[end - 1])) {
|
315
|
+
end--
|
316
|
+
}
|
317
|
+
// 如果我们在多字节字符的起始处中止,则再次前移
|
318
|
+
if (end > start && isUTF8MultiByteStart(uint8Array[end - 1])) {
|
319
|
+
end--
|
320
|
+
}
|
321
|
+
}
|
322
|
+
|
323
|
+
const slice = uint8Array.subarray(start, end)
|
324
|
+
slices.push(lib.decoder.decode(slice))
|
325
|
+
start = end // 设置下次分片的起始位置
|
326
|
+
}
|
327
|
+
|
328
|
+
return slices
|
329
|
+
}
|
330
|
+
|
331
|
+
export function isType(data, type = undefined) { // 判断数据是否为 type, 或返回 type
|
332
|
+
const dataType = Object.prototype.toString.call(data).match(/\s(.+)]/)[1].toLowerCase()
|
333
|
+
return type ? (dataType === type.toLowerCase()) : dataType
|
334
|
+
}
|
335
|
+
|
336
|
+
/**
|
337
|
+
* 判断是否为空值
|
338
|
+
* @param {*} value 要判断的值
|
339
|
+
*/
|
340
|
+
export function isEmpty(value) {
|
341
|
+
return [NaN, null, undefined, ``, [], {}].some((emptyItem) =>
|
342
|
+
typeof value === `string` && value
|
343
|
+
? false
|
344
|
+
: JSON.stringify(value) === JSON.stringify(emptyItem),
|
345
|
+
)
|
346
|
+
}
|
347
|
+
|
348
|
+
/**
|
349
|
+
* 删除空值
|
350
|
+
* @param {object} obj 要处理的数据
|
351
|
+
*/
|
352
|
+
export function removeEmpty(obj) {
|
353
|
+
return JSON.parse(JSON.stringify(obj), (key, value) => {
|
354
|
+
if (isEmpty(value) === false && Array.isArray(value)) {
|
355
|
+
value = value.filter((v) => !isEmpty(v))
|
356
|
+
}
|
357
|
+
return isEmpty(value) ? undefined : value
|
358
|
+
})
|
359
|
+
}
|
360
|
+
|
361
|
+
/**
|
362
|
+
* 函数缓存器,相同参数只会执行一次
|
363
|
+
* @param {*} fn
|
364
|
+
* @returns
|
365
|
+
*/
|
366
|
+
export function memoize(fn) {
|
367
|
+
const cache = new Map() // 使用Map来存储缓存结果
|
368
|
+
|
369
|
+
function memoized(...args) {
|
370
|
+
const key = JSON.stringify(args) // 将参数转换为字符串作为缓存的键
|
371
|
+
if (cache.has(key)) {
|
372
|
+
return cache.get(key) // 如果缓存中已存在,直接返回缓存的结果
|
373
|
+
}
|
374
|
+
|
375
|
+
const result = fn.apply(this, args) // 否则,调用函数并存储结果
|
376
|
+
cache.set(key, result)
|
377
|
+
return result
|
378
|
+
}
|
379
|
+
|
380
|
+
// 添加一个方法来清除缓存
|
381
|
+
memoized.clearCache = function() {
|
382
|
+
cache.clear()
|
383
|
+
}
|
384
|
+
|
385
|
+
return memoized
|
386
|
+
}
|