ph-utils 0.5.1 → 0.6.1
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/README.md +7 -7
- package/lib/clipboard.d.ts +11 -0
- package/lib/clipboard.js +101 -0
- package/lib/date.d.ts +7 -3
- package/lib/date.js +3 -3
- package/lib/file.d.ts +10 -10
- package/lib/file.js +31 -42
- package/lib/server.d.ts +7 -29
- package/lib/server.js +37 -62
- package/package.json +1 -4
package/README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
## ph-utils
|
2
|
-
|
3
|
-
整理了 js 前后端开发(web + nodejs)时常用的一些工具;[详细文档](https://gitee.com/towardly/ph/wikis/Home?sort_id=4035190)
|
4
|
-
|
5
|
-
### 包含如下工具文件
|
6
|
-
|
7
|
-
`index` 基础工具类、`date` 跟日期相关的工具类、`file` 文件操作相关工具类[**服务端**]、`server` 服务端工具类、`validator` 数据验证、`dom` 浏览器节点操作相关[**前端**]、`web` 一些只适用于前端相关的工具、`color` 颜色相关工具
|
1
|
+
## ph-utils
|
2
|
+
|
3
|
+
整理了 js 前后端开发(web + nodejs)时常用的一些工具;[详细文档](https://gitee.com/towardly/ph/wikis/Home?sort_id=4035190)
|
4
|
+
|
5
|
+
### 包含如下工具文件
|
6
|
+
|
7
|
+
`index` 基础工具类、`date` 跟日期相关的工具类、`file` 文件操作相关工具类[**服务端**]、`server` 服务端工具类、`validator` 数据验证、`dom` 浏览器节点操作相关[**前端**]、`web` 一些只适用于前端相关的工具、`color` 颜色相关工具
|
@@ -0,0 +1,11 @@
|
|
1
|
+
/**
|
2
|
+
* 复制数据, 可以从多种类型的数据
|
3
|
+
* 1. 直接复制文本: await copy("待复制的文本")
|
4
|
+
* 2. 复制节点上的 data-copy-text:
|
5
|
+
* <button data-copy-text="这是待复制的文本">复制</button>
|
6
|
+
* await copy(e.target) // or await copy("#a") or await copy(document.querySelector('#a'))
|
7
|
+
* 3. 直接复制节点本身数据: await copy('#a')
|
8
|
+
* @param {string | HTMLElement} source 复制源, 从中解析待复制的数据
|
9
|
+
* @returns {Promise<boolean>} 是否复制成功
|
10
|
+
*/
|
11
|
+
export declare function copy(source: string | HTMLElement): Promise<boolean>;
|
package/lib/clipboard.js
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
/**
|
2
|
+
* 创建一个临时节点缓存待复制数据
|
3
|
+
* @param {String} value - 待复制文本
|
4
|
+
* @return {HTMLElement}
|
5
|
+
*/
|
6
|
+
function createFakeElement(value) {
|
7
|
+
const fakeElement = document.createElement("textarea");
|
8
|
+
fakeElement.style.border = "0";
|
9
|
+
fakeElement.style.padding = "0";
|
10
|
+
fakeElement.style.margin = "0";
|
11
|
+
fakeElement.style.position = "absolute";
|
12
|
+
fakeElement.style.left = "-9999px";
|
13
|
+
fakeElement.style.top = "-9999";
|
14
|
+
fakeElement.setAttribute("readonly", "");
|
15
|
+
fakeElement.value = value;
|
16
|
+
return fakeElement;
|
17
|
+
}
|
18
|
+
/** 通过执行 execCommand 来执行复制 */
|
19
|
+
function copyFromCommand(text) {
|
20
|
+
// 添加节点
|
21
|
+
const fakeEl = createFakeElement(text);
|
22
|
+
document.body.append(fakeEl);
|
23
|
+
fakeEl.focus();
|
24
|
+
fakeEl.select();
|
25
|
+
// 执行复制
|
26
|
+
const res = document.execCommand("copy");
|
27
|
+
fakeEl.remove(); // 删除节点
|
28
|
+
return Promise.resolve(res);
|
29
|
+
}
|
30
|
+
/** 使用 navigator.clipboard 复制 */
|
31
|
+
function copyFromClipboard(text) {
|
32
|
+
const theClipboard = navigator.clipboard;
|
33
|
+
if (theClipboard != null) {
|
34
|
+
return theClipboard
|
35
|
+
.writeText(text)
|
36
|
+
.then(() => {
|
37
|
+
Promise.resolve(true);
|
38
|
+
})
|
39
|
+
.catch(() => Promise.resolve(false));
|
40
|
+
}
|
41
|
+
return Promise.resolve(false);
|
42
|
+
}
|
43
|
+
/** 解析待复制的文本 */
|
44
|
+
function parseCopyText(source) {
|
45
|
+
let copyText = null; // 待复制文本
|
46
|
+
let sourceEl = null;
|
47
|
+
// 获取待复制数据
|
48
|
+
if (typeof source === "string") {
|
49
|
+
// 从节点拿数据
|
50
|
+
if (source.startsWith("#") || source.startsWith(".")) {
|
51
|
+
sourceEl = document.querySelector(source);
|
52
|
+
if (sourceEl == null) {
|
53
|
+
copyText = source;
|
54
|
+
}
|
55
|
+
}
|
56
|
+
else {
|
57
|
+
copyText = source;
|
58
|
+
}
|
59
|
+
}
|
60
|
+
if (source instanceof HTMLElement) {
|
61
|
+
sourceEl = source;
|
62
|
+
}
|
63
|
+
// 从节点获取待复制数据
|
64
|
+
if (sourceEl != null) {
|
65
|
+
if (sourceEl.hasAttribute("data-copy-text")) {
|
66
|
+
copyText = sourceEl.getAttribute("data-copy-text");
|
67
|
+
}
|
68
|
+
else {
|
69
|
+
const tagName = sourceEl.tagName;
|
70
|
+
if (tagName === "INPUT" || tagName === "TEXTAREA") {
|
71
|
+
copyText = sourceEl.value;
|
72
|
+
}
|
73
|
+
else {
|
74
|
+
copyText = sourceEl.textContent;
|
75
|
+
}
|
76
|
+
}
|
77
|
+
}
|
78
|
+
return copyText;
|
79
|
+
}
|
80
|
+
/**
|
81
|
+
* 复制数据, 可以从多种类型的数据
|
82
|
+
* 1. 直接复制文本: await copy("待复制的文本")
|
83
|
+
* 2. 复制节点上的 data-copy-text:
|
84
|
+
* <button data-copy-text="这是待复制的文本">复制</button>
|
85
|
+
* await copy(e.target) // or await copy("#a") or await copy(document.querySelector('#a'))
|
86
|
+
* 3. 直接复制节点本身数据: await copy('#a')
|
87
|
+
* @param {string | HTMLElement} source 复制源, 从中解析待复制的数据
|
88
|
+
* @returns {Promise<boolean>} 是否复制成功
|
89
|
+
*/
|
90
|
+
export async function copy(source) {
|
91
|
+
// 待复制文本
|
92
|
+
const copyText = parseCopyText(source);
|
93
|
+
if (copyText == null) {
|
94
|
+
return Promise.resolve(false);
|
95
|
+
}
|
96
|
+
const v = await copyFromClipboard(copyText);
|
97
|
+
if (v === false) {
|
98
|
+
return copyFromCommand(copyText);
|
99
|
+
}
|
100
|
+
return Promise.resolve(true);
|
101
|
+
}
|
package/lib/date.d.ts
CHANGED
@@ -12,9 +12,9 @@ export declare function format(date?: Date | string | number | null, pattern?: s
|
|
12
12
|
export declare function parse(date?: Date | string | number | null): Date;
|
13
13
|
/**
|
14
14
|
* 设置日期的开始或者结束的点
|
15
|
-
* @param
|
16
|
-
* @param
|
17
|
-
* @param
|
15
|
+
* @param date 日期,能够被 parse 解析的日期
|
16
|
+
* @param unit 单位,Date|date, 默认为 Date
|
17
|
+
* @param isEnd true则为 endOf
|
18
18
|
*/
|
19
19
|
export declare function dateOf(date?: Date | string | number, unit?: string, isEnd?: boolean): Date;
|
20
20
|
/**
|
@@ -45,6 +45,10 @@ export declare function timeStamp(ctime?: Date | string | number, pre?: "s" | "m
|
|
45
45
|
* @param unit 需要添加的单位,date、month、year、hours、minute、second
|
46
46
|
*
|
47
47
|
* 查阅文档: {@link https://gitee.com/towardly/ph/wikis/utils/date ph-utils}
|
48
|
+
*
|
49
|
+
* @example <caption>1. 分钟加1并格式化显示时间</caption>
|
50
|
+
*
|
51
|
+
* add(new Date(), 1, 'minute', 'HHMMss')
|
48
52
|
*/
|
49
53
|
export declare function add(date: Date | string | number | null, num: number, unit: string): Date;
|
50
54
|
/**
|
package/lib/date.js
CHANGED
@@ -131,9 +131,9 @@ export function parse(date) {
|
|
131
131
|
}
|
132
132
|
/**
|
133
133
|
* 设置日期的开始或者结束的点
|
134
|
-
* @param
|
135
|
-
* @param
|
136
|
-
* @param
|
134
|
+
* @param date 日期,能够被 parse 解析的日期
|
135
|
+
* @param unit 单位,Date|date, 默认为 Date
|
136
|
+
* @param isEnd true则为 endOf
|
137
137
|
*/
|
138
138
|
export function dateOf(date, unit, isEnd = false) {
|
139
139
|
/* 如果是设置某一天的开始时刻, 就需要将时、分、秒、毫秒设置为0,依次类推设置 */
|
package/lib/file.d.ts
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
/**
|
2
|
-
*
|
3
|
-
* @
|
4
|
-
*
|
5
|
-
*
|
6
|
-
*
|
7
|
-
*
|
8
|
-
*
|
9
|
-
* @returns
|
2
|
+
* 读取文件内容
|
3
|
+
* @example <caption>1. 读取JSON文件, 内容为字符串列表</caption>
|
4
|
+
* read<string[]>('a.json', []);
|
5
|
+
* @example <caption>2. 读取JSON文件, 内容为对象</caption>
|
6
|
+
* read<{ name: string }>('b.json', {});
|
7
|
+
* @param filepath 文件路径
|
8
|
+
* @param defaultValue 文件不存在时默认值, 不传则抛异常, 如果传递的是对象形式则会将结果转换为 JSON
|
9
|
+
* @returns 文件内容
|
10
10
|
*/
|
11
|
-
export declare function
|
11
|
+
export declare function read<T>(filepath: string, defaultValue?: T): Promise<any>;
|
12
12
|
/**
|
13
13
|
* 写入 JSON 格式的数据到文件
|
14
14
|
* @param file 待写入的文件
|
@@ -20,7 +20,7 @@ export declare function readJSON<T>(filepath: string, defaultValue?: T): Promise
|
|
20
20
|
export declare function write(file: string, data: any, opts?: {
|
21
21
|
json: boolean;
|
22
22
|
format: boolean;
|
23
|
-
}): Promise<
|
23
|
+
}): Promise<void>;
|
24
24
|
/**
|
25
25
|
* 根据文件的 stat 获取文件的 etag
|
26
26
|
* @param filePath 文件地址
|
package/lib/file.js
CHANGED
@@ -1,32 +1,30 @@
|
|
1
1
|
/** nodejs 文件操作工具类 */
|
2
2
|
import path from "node:path";
|
3
|
-
import fs from "node:fs";
|
3
|
+
import fs from "node:fs/promises";
|
4
4
|
/**
|
5
|
-
*
|
6
|
-
* @
|
7
|
-
*
|
8
|
-
*
|
9
|
-
*
|
10
|
-
*
|
11
|
-
*
|
12
|
-
* @returns
|
5
|
+
* 读取文件内容
|
6
|
+
* @example <caption>1. 读取JSON文件, 内容为字符串列表</caption>
|
7
|
+
* read<string[]>('a.json', []);
|
8
|
+
* @example <caption>2. 读取JSON文件, 内容为对象</caption>
|
9
|
+
* read<{ name: string }>('b.json', {});
|
10
|
+
* @param filepath 文件路径
|
11
|
+
* @param defaultValue 文件不存在时默认值, 不传则抛异常, 如果传递的是对象形式则会将结果转换为 JSON
|
12
|
+
* @returns 文件内容
|
13
13
|
*/
|
14
|
-
export function
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
});
|
29
|
-
});
|
14
|
+
export async function read(filepath, defaultValue) {
|
15
|
+
let content;
|
16
|
+
try {
|
17
|
+
content = await fs.readFile(filepath, "utf8");
|
18
|
+
if (defaultValue != null && typeof defaultValue === "object") {
|
19
|
+
return JSON.parse(content);
|
20
|
+
}
|
21
|
+
}
|
22
|
+
catch (error) {
|
23
|
+
if (defaultValue === undefined) {
|
24
|
+
throw error;
|
25
|
+
}
|
26
|
+
return defaultValue;
|
27
|
+
}
|
30
28
|
}
|
31
29
|
/**
|
32
30
|
* 写入 JSON 格式的数据到文件
|
@@ -36,22 +34,13 @@ export function readJSON(filepath, defaultValue) {
|
|
36
34
|
* @property opts.json 是否写入 JSON 格式的数据,写入数据时对数据进行 JSON 格式化,默认为:true
|
37
35
|
* @property opts.format 是否在写入 json 数据时,将 JSON 数据格式化2个空格写入, 默认为 true
|
38
36
|
*/
|
39
|
-
export function write(file, data, opts) {
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
fs.writeFile(path.resolve(file), writeData, (err) => {
|
47
|
-
if (err) {
|
48
|
-
reject(err);
|
49
|
-
}
|
50
|
-
else {
|
51
|
-
resolve(0);
|
52
|
-
}
|
53
|
-
});
|
54
|
-
});
|
37
|
+
export async function write(file, data, opts) {
|
38
|
+
let writeData = data.toString();
|
39
|
+
opts = { json: true, format: true, ...opts };
|
40
|
+
if (opts.json === true && typeof data === "object") {
|
41
|
+
writeData = JSON.stringify(data, null, opts.format === true ? 2 : 0);
|
42
|
+
}
|
43
|
+
return await fs.writeFile(path.resolve(file), writeData);
|
55
44
|
}
|
56
45
|
/**
|
57
46
|
* 根据文件的 stat 获取文件的 etag
|
@@ -59,6 +48,6 @@ export function write(file, data, opts) {
|
|
59
48
|
* @returns file stat etag
|
60
49
|
*/
|
61
50
|
export async function statTag(filePath) {
|
62
|
-
let stat = await fs.
|
51
|
+
let stat = await fs.stat(filePath);
|
63
52
|
return `${stat.size.toString(16)}-${stat.mtimeMs.toString(16)}`;
|
64
53
|
}
|
package/lib/server.d.ts
CHANGED
@@ -1,32 +1,10 @@
|
|
1
|
+
/// <reference types="node" />
|
2
|
+
import type { SpawnOptions } from "node:child_process";
|
1
3
|
/**
|
2
4
|
* 执行命令
|
3
|
-
* @param
|
4
|
-
* @
|
5
|
+
* @param command 待执行的命令
|
6
|
+
* @param args 命令参数
|
5
7
|
*/
|
6
|
-
export declare function exec(
|
7
|
-
|
8
|
-
|
9
|
-
cwd?: string;
|
10
|
-
/** 每一行的输出 */
|
11
|
-
data?: (lineText?: string) => void;
|
12
|
-
/** 错误输出 */
|
13
|
-
error?: (err: Error) => void;
|
14
|
-
/** 最终结果 */
|
15
|
-
finally?: (err?: Error) => void;
|
16
|
-
}
|
17
|
-
/**
|
18
|
-
* 执行 spawn 命令
|
19
|
-
* @param command 执行的命令: 例如: git
|
20
|
-
* @param args 命令参数: ['clone', 'https://xxxx.git']
|
21
|
-
* @param options 参数
|
22
|
-
*/
|
23
|
-
export declare function spawnCmd(command: string, args?: string[], options?: SpawnCmdOptions): void;
|
24
|
-
/**
|
25
|
-
* 执行 spawn 命令
|
26
|
-
* @param command 执行的命令: 例如: git
|
27
|
-
* @param args 命令参数: ['clone', 'https://xxxx.git']
|
28
|
-
* @param options 参数
|
29
|
-
* @returns
|
30
|
-
*/
|
31
|
-
export declare function spawnPromise(command: string, args?: string[], options?: SpawnCmdOptions): Promise<unknown>;
|
32
|
-
export {};
|
8
|
+
export declare function exec(command: string, args?: string[]): Promise<string>;
|
9
|
+
export declare function exec(command: string, options?: SpawnOptions): Promise<string>;
|
10
|
+
export declare function exec(command: string, args?: string[], options?: SpawnOptions): Promise<string>;
|
package/lib/server.js
CHANGED
@@ -1,77 +1,52 @@
|
|
1
|
-
import {
|
2
|
-
import { isBlank } from './index.js';
|
1
|
+
import { spawn } from "node:child_process";
|
3
2
|
/**
|
4
3
|
* 执行命令
|
5
4
|
* @param cmd 执行的命令
|
6
5
|
* @returns
|
7
6
|
*/
|
8
|
-
export function exec(
|
7
|
+
export function exec(command, ...params) {
|
9
8
|
return new Promise((resolve, reject) => {
|
10
|
-
|
11
|
-
|
12
|
-
|
9
|
+
let argvs = [];
|
10
|
+
const commandItems = command.split(" ");
|
11
|
+
const cmd = commandItems.shift();
|
12
|
+
if (commandItems.length > 0) {
|
13
|
+
argvs = commandItems;
|
14
|
+
}
|
15
|
+
let opts = undefined;
|
16
|
+
if (params[0] != null) {
|
17
|
+
if (params[0] instanceof Array) {
|
18
|
+
argvs.push(...params[0]);
|
19
|
+
if (params[1] != null) {
|
20
|
+
opts = params[1];
|
21
|
+
}
|
13
22
|
}
|
14
23
|
else {
|
15
|
-
|
16
|
-
reject(new Error(stderr));
|
17
|
-
}
|
18
|
-
else {
|
19
|
-
resolve((isBlank(stdout) ? stderr : stdout).trim());
|
20
|
-
}
|
24
|
+
opts = params[0];
|
21
25
|
}
|
22
|
-
});
|
23
|
-
});
|
24
|
-
}
|
25
|
-
/**
|
26
|
-
* 执行 spawn 命令
|
27
|
-
* @param command 执行的命令: 例如: git
|
28
|
-
* @param args 命令参数: ['clone', 'https://xxxx.git']
|
29
|
-
* @param options 参数
|
30
|
-
*/
|
31
|
-
export function spawnCmd(command, args, options) {
|
32
|
-
const spawnClient = spawn(command, args, { cwd: options?.cwd });
|
33
|
-
let err;
|
34
|
-
spawnClient.stdout.on('data', (chunk) => {
|
35
|
-
if (options?.data != null && typeof options.data === 'function') {
|
36
|
-
options.data(chunk);
|
37
|
-
}
|
38
|
-
});
|
39
|
-
spawnClient.stderr.on('error', (error) => {
|
40
|
-
err = error;
|
41
|
-
if (options?.error && typeof options.error === 'function') {
|
42
|
-
options.error(error);
|
43
|
-
}
|
44
|
-
});
|
45
|
-
spawnClient.on('error', (error) => {
|
46
|
-
err = error;
|
47
|
-
if (options?.error && typeof options.error === 'function') {
|
48
|
-
options.error(error);
|
49
26
|
}
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
return new Promise((resolve, reject) => {
|
66
|
-
options = options || {};
|
67
|
-
options.finally = (err) => {
|
68
|
-
if (err == null) {
|
69
|
-
resolve(1);
|
27
|
+
const prs = spawn(cmd, argvs, opts);
|
28
|
+
let msg = [];
|
29
|
+
let error;
|
30
|
+
prs.stderr.on("data", (chunk) => {
|
31
|
+
msg.push(chunk.toString("utf-8"));
|
32
|
+
});
|
33
|
+
prs.stdout.on("data", (chunk) => {
|
34
|
+
msg.push(chunk.toString("utf-8"));
|
35
|
+
});
|
36
|
+
prs.on("error", (err) => {
|
37
|
+
error = err;
|
38
|
+
});
|
39
|
+
prs.on("close", (code, signal) => {
|
40
|
+
if (code === 0) {
|
41
|
+
resolve(msg.join("\n"));
|
70
42
|
}
|
71
43
|
else {
|
72
|
-
|
44
|
+
if (error == null) {
|
45
|
+
error = new Error(msg[msg.length - 1]);
|
46
|
+
}
|
47
|
+
error.errno = code;
|
48
|
+
reject(error);
|
73
49
|
}
|
74
|
-
};
|
75
|
-
spawnCmd(command, args, options);
|
50
|
+
});
|
76
51
|
});
|
77
52
|
}
|
package/package.json
CHANGED
@@ -52,7 +52,7 @@
|
|
52
52
|
},
|
53
53
|
"./*": "./lib/*"
|
54
54
|
},
|
55
|
-
"version": "0.
|
55
|
+
"version": "0.6.1",
|
56
56
|
"type": "module",
|
57
57
|
"repository": {
|
58
58
|
"type": "git",
|
@@ -80,9 +80,6 @@
|
|
80
80
|
"dom",
|
81
81
|
"file"
|
82
82
|
],
|
83
|
-
"dependencies": {
|
84
|
-
"@ctrl/tinycolor": "^4.1.0"
|
85
|
-
},
|
86
83
|
"scripts": {
|
87
84
|
"build": "node scripts/build.js"
|
88
85
|
}
|