@jayfong/x-utils 1.0.0-202407012223
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/_cjs/ai.js +80 -0
- package/_cjs/cli.js +51 -0
- package/_cjs/clipboard.js +65 -0
- package/_cjs/cmd.js +42 -0
- package/_cjs/crawler.js +341 -0
- package/_cjs/csv.js +97 -0
- package/_cjs/data_set.js +115 -0
- package/_cjs/env.js +63 -0
- package/_cjs/fs.js +35 -0
- package/_cjs/html.js +62 -0
- package/_cjs/http_server.js +56 -0
- package/_cjs/json.js +49 -0
- package/_cjs/ksb.js +217 -0
- package/_cjs/number.js +39 -0
- package/_cjs/ocr.js +74 -0
- package/_cjs/path.js +35 -0
- package/_cjs/pinyin.js +32 -0
- package/_cjs/puppeteer.js +271 -0
- package/_cjs/runner.js +88 -0
- package/_cjs/task.js +41 -0
- package/_cjs/text.js +136 -0
- package/_cjs/unicode/lib_unicode.js +58643 -0
- package/_cjs/unicode/unicode.js +441 -0
- package/_cjs/upload.js +100 -0
- package/_cjs/yaml.js +52 -0
- package/ai.d.ts +26 -0
- package/ai.js +60 -0
- package/cli.d.ts +14 -0
- package/cli.js +21 -0
- package/clipboard.d.ts +6 -0
- package/clipboard.js +35 -0
- package/cmd.d.ts +5 -0
- package/cmd.js +22 -0
- package/crawler.d.ts +162 -0
- package/crawler.js +332 -0
- package/csv.d.ts +14 -0
- package/csv.js +67 -0
- package/data_set.d.ts +22 -0
- package/data_set.js +85 -0
- package/env.d.ts +4 -0
- package/env.js +32 -0
- package/fs.d.ts +2 -0
- package/fs.js +5 -0
- package/html.d.ts +7 -0
- package/html.js +45 -0
- package/http_server.d.ts +10 -0
- package/http_server.js +26 -0
- package/json.d.ts +6 -0
- package/json.js +19 -0
- package/ksb.d.ts +32 -0
- package/ksb.js +197 -0
- package/number.d.ts +3 -0
- package/number.js +9 -0
- package/ocr.d.ts +10 -0
- package/ocr.js +54 -0
- package/ofd.d.ts +1 -0
- package/package.json +158 -0
- package/path.d.ts +2 -0
- package/path.js +5 -0
- package/pinyin.d.ts +3 -0
- package/pinyin.js +12 -0
- package/puppeteer.d.ts +38 -0
- package/puppeteer.js +241 -0
- package/runner.d.ts +13 -0
- package/runner.js +58 -0
- package/task.d.ts +6 -0
- package/task.js +21 -0
- package/text.d.ts +63 -0
- package/text.js +119 -0
- package/unicode/lib_unicode.d.ts +4 -0
- package/unicode/lib_unicode.js +58623 -0
- package/unicode/unicode.d.ts +347 -0
- package/unicode/unicode.js +426 -0
- package/upload.d.ts +22 -0
- package/upload.js +70 -0
- package/yaml.d.ts +6 -0
- package/yaml.js +22 -0
package/package.json
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@jayfong/x-utils",
|
|
3
|
+
"description": "123456",
|
|
4
|
+
"version": "1.0.0-202407012223",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"dependencies": {
|
|
7
|
+
"vtils": "^4.115.0",
|
|
8
|
+
"cli-table3": "^0.6.3",
|
|
9
|
+
"inquirer": "^9.2.13",
|
|
10
|
+
"clipboardy": "^4.0.0",
|
|
11
|
+
"execa": "^8.0.1",
|
|
12
|
+
"@jayfong/x-request": "^2.35.2",
|
|
13
|
+
"node-html-parser": "^6.1.12",
|
|
14
|
+
"csv": "^6.3.6",
|
|
15
|
+
"exit-hook": "^4.0.0",
|
|
16
|
+
"fs-extra": "^11.2.0",
|
|
17
|
+
"dotenv": "^16.4.5",
|
|
18
|
+
"docx": "^8.5.0",
|
|
19
|
+
"image-dimensions": "^2.3.0",
|
|
20
|
+
"@fastify/cors": "^9.0.1",
|
|
21
|
+
"fastify": "^4.26.0",
|
|
22
|
+
"nzh": "^1.0.12",
|
|
23
|
+
"pathe": "^1.1.2",
|
|
24
|
+
"pinyin-pro": "^3.19.4",
|
|
25
|
+
"puppeteer": "^22.3.0",
|
|
26
|
+
"consola": "^3.2.3",
|
|
27
|
+
"html-entities": "^2.5.2",
|
|
28
|
+
"file-type": "^19.0.0",
|
|
29
|
+
"sharp": "^0.32.5",
|
|
30
|
+
"yaml": "^2.3.4"
|
|
31
|
+
},
|
|
32
|
+
"exports": {
|
|
33
|
+
"./package.json": "./package.json",
|
|
34
|
+
"./ai": {
|
|
35
|
+
"require": "./_cjs/ai.js",
|
|
36
|
+
"import": "./ai.js",
|
|
37
|
+
"types": "./ai.d.ts"
|
|
38
|
+
},
|
|
39
|
+
"./cli": {
|
|
40
|
+
"require": "./_cjs/cli.js",
|
|
41
|
+
"import": "./cli.js",
|
|
42
|
+
"types": "./cli.d.ts"
|
|
43
|
+
},
|
|
44
|
+
"./clipboard": {
|
|
45
|
+
"require": "./_cjs/clipboard.js",
|
|
46
|
+
"import": "./clipboard.js",
|
|
47
|
+
"types": "./clipboard.d.ts"
|
|
48
|
+
},
|
|
49
|
+
"./cmd": {
|
|
50
|
+
"require": "./_cjs/cmd.js",
|
|
51
|
+
"import": "./cmd.js",
|
|
52
|
+
"types": "./cmd.d.ts"
|
|
53
|
+
},
|
|
54
|
+
"./crawler": {
|
|
55
|
+
"require": "./_cjs/crawler.js",
|
|
56
|
+
"import": "./crawler.js",
|
|
57
|
+
"types": "./crawler.d.ts"
|
|
58
|
+
},
|
|
59
|
+
"./csv": {
|
|
60
|
+
"require": "./_cjs/csv.js",
|
|
61
|
+
"import": "./csv.js",
|
|
62
|
+
"types": "./csv.d.ts"
|
|
63
|
+
},
|
|
64
|
+
"./data_set": {
|
|
65
|
+
"require": "./_cjs/data_set.js",
|
|
66
|
+
"import": "./data_set.js",
|
|
67
|
+
"types": "./data_set.d.ts"
|
|
68
|
+
},
|
|
69
|
+
"./env": {
|
|
70
|
+
"require": "./_cjs/env.js",
|
|
71
|
+
"import": "./env.js",
|
|
72
|
+
"types": "./env.d.ts"
|
|
73
|
+
},
|
|
74
|
+
"./fs": {
|
|
75
|
+
"require": "./_cjs/fs.js",
|
|
76
|
+
"import": "./fs.js",
|
|
77
|
+
"types": "./fs.d.ts"
|
|
78
|
+
},
|
|
79
|
+
"./html": {
|
|
80
|
+
"require": "./_cjs/html.js",
|
|
81
|
+
"import": "./html.js",
|
|
82
|
+
"types": "./html.d.ts"
|
|
83
|
+
},
|
|
84
|
+
"./http_server": {
|
|
85
|
+
"require": "./_cjs/http_server.js",
|
|
86
|
+
"import": "./http_server.js",
|
|
87
|
+
"types": "./http_server.d.ts"
|
|
88
|
+
},
|
|
89
|
+
"./json": {
|
|
90
|
+
"require": "./_cjs/json.js",
|
|
91
|
+
"import": "./json.js",
|
|
92
|
+
"types": "./json.d.ts"
|
|
93
|
+
},
|
|
94
|
+
"./ksb": {
|
|
95
|
+
"require": "./_cjs/ksb.js",
|
|
96
|
+
"import": "./ksb.js",
|
|
97
|
+
"types": "./ksb.d.ts"
|
|
98
|
+
},
|
|
99
|
+
"./number": {
|
|
100
|
+
"require": "./_cjs/number.js",
|
|
101
|
+
"import": "./number.js",
|
|
102
|
+
"types": "./number.d.ts"
|
|
103
|
+
},
|
|
104
|
+
"./ocr": {
|
|
105
|
+
"require": "./_cjs/ocr.js",
|
|
106
|
+
"import": "./ocr.js",
|
|
107
|
+
"types": "./ocr.d.ts"
|
|
108
|
+
},
|
|
109
|
+
"./path": {
|
|
110
|
+
"require": "./_cjs/path.js",
|
|
111
|
+
"import": "./path.js",
|
|
112
|
+
"types": "./path.d.ts"
|
|
113
|
+
},
|
|
114
|
+
"./pinyin": {
|
|
115
|
+
"require": "./_cjs/pinyin.js",
|
|
116
|
+
"import": "./pinyin.js",
|
|
117
|
+
"types": "./pinyin.d.ts"
|
|
118
|
+
},
|
|
119
|
+
"./puppeteer": {
|
|
120
|
+
"require": "./_cjs/puppeteer.js",
|
|
121
|
+
"import": "./puppeteer.js",
|
|
122
|
+
"types": "./puppeteer.d.ts"
|
|
123
|
+
},
|
|
124
|
+
"./runner": {
|
|
125
|
+
"require": "./_cjs/runner.js",
|
|
126
|
+
"import": "./runner.js",
|
|
127
|
+
"types": "./runner.d.ts"
|
|
128
|
+
},
|
|
129
|
+
"./task": {
|
|
130
|
+
"require": "./_cjs/task.js",
|
|
131
|
+
"import": "./task.js",
|
|
132
|
+
"types": "./task.d.ts"
|
|
133
|
+
},
|
|
134
|
+
"./text": {
|
|
135
|
+
"require": "./_cjs/text.js",
|
|
136
|
+
"import": "./text.js",
|
|
137
|
+
"types": "./text.d.ts"
|
|
138
|
+
},
|
|
139
|
+
"./upload": {
|
|
140
|
+
"require": "./_cjs/upload.js",
|
|
141
|
+
"import": "./upload.js",
|
|
142
|
+
"types": "./upload.d.ts"
|
|
143
|
+
},
|
|
144
|
+
"./yaml": {
|
|
145
|
+
"require": "./_cjs/yaml.js",
|
|
146
|
+
"import": "./yaml.js",
|
|
147
|
+
"types": "./yaml.d.ts"
|
|
148
|
+
},
|
|
149
|
+
"./unicode": {
|
|
150
|
+
"require": "./_cjs/unicode/unicode.js",
|
|
151
|
+
"import": "./unicode/unicode.js",
|
|
152
|
+
"types": "./unicode/unicode.d.ts"
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
"publishConfig": {
|
|
156
|
+
"access": "public"
|
|
157
|
+
}
|
|
158
|
+
}
|
package/path.d.ts
ADDED
package/path.js
ADDED
package/pinyin.d.ts
ADDED
package/pinyin.js
ADDED
package/puppeteer.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/// <reference types="lodash" />
|
|
2
|
+
import { Browser, ElementHandle, Page } from 'puppeteer';
|
|
3
|
+
export type XPuppeteerBrowser = Browser;
|
|
4
|
+
export type XPuppeteerPage = Page;
|
|
5
|
+
export type XPuppeteerOpenBrowserOptions = {
|
|
6
|
+
headful?: boolean;
|
|
7
|
+
proxy?: string;
|
|
8
|
+
dataDir?: string;
|
|
9
|
+
};
|
|
10
|
+
export type XPuppeteerOpenPageOptions = {
|
|
11
|
+
match?: string | RegExp;
|
|
12
|
+
blockAssets?: boolean;
|
|
13
|
+
referer?: string;
|
|
14
|
+
};
|
|
15
|
+
export type XPuppeteerGetPageHtmlOptions = XPuppeteerOpenBrowserOptions & XPuppeteerOpenPageOptions & {
|
|
16
|
+
instanceId?: string | number;
|
|
17
|
+
useSystemBrowser?: boolean;
|
|
18
|
+
afterOpenPage?: (payload: {
|
|
19
|
+
browser: XPuppeteer;
|
|
20
|
+
page: XPuppeteerPage;
|
|
21
|
+
}) => any;
|
|
22
|
+
};
|
|
23
|
+
export declare class XPuppeteer {
|
|
24
|
+
browser: Browser;
|
|
25
|
+
connectSystemBrowser(): Promise<Browser>;
|
|
26
|
+
openBrowser(options?: XPuppeteerOpenBrowserOptions): Promise<Browser>;
|
|
27
|
+
openPage(urlOrHtml: string, options?: XPuppeteerOpenPageOptions): Promise<Page>;
|
|
28
|
+
input(page: Page, selector: string, textOrHtml: string, frameIndex?: number): Promise<void>;
|
|
29
|
+
selectAll(page: Page): Promise<void>;
|
|
30
|
+
paste(page: Page): Promise<void>;
|
|
31
|
+
queryByText<T extends Node = HTMLDivElement>(page: Page, text: string | RegExp | ((el: T) => boolean)): Promise<ElementHandle<T>>;
|
|
32
|
+
disconnect(): Promise<void>;
|
|
33
|
+
close(): Promise<void>;
|
|
34
|
+
static openBrowser(options?: XPuppeteerOpenBrowserOptions): Promise<XPuppeteer>;
|
|
35
|
+
static openPage(urlOrHtml: string, options?: XPuppeteerGetPageHtmlOptions): Promise<Page>;
|
|
36
|
+
static getPageHtmlXp: ((options: XPuppeteerGetPageHtmlOptions) => Promise<XPuppeteer>) & import("lodash").MemoizedFunction;
|
|
37
|
+
static getPageHtml(url: string, options?: XPuppeteerGetPageHtmlOptions): Promise<string>;
|
|
38
|
+
}
|
package/puppeteer.js
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import { xr } from "@jayfong/x-request";
|
|
2
|
+
import { asyncExitHook } from "exit-hook";
|
|
3
|
+
import puppeteer from "puppeteer";
|
|
4
|
+
import { isFunction, isRegExp, memoize } from "vtils";
|
|
5
|
+
import { XClipboard } from "./clipboard.js";
|
|
6
|
+
class XPuppeteer {
|
|
7
|
+
browser;
|
|
8
|
+
async connectSystemBrowser() {
|
|
9
|
+
const {
|
|
10
|
+
data: { webSocketDebuggerUrl }
|
|
11
|
+
} = await xr.getJson({
|
|
12
|
+
url: "http://localhost:9222/json/version"
|
|
13
|
+
});
|
|
14
|
+
const browser = await puppeteer.connect({
|
|
15
|
+
browserWSEndpoint: webSocketDebuggerUrl,
|
|
16
|
+
// 都设为 0 以保持原有的宽高
|
|
17
|
+
defaultViewport: {
|
|
18
|
+
width: 0,
|
|
19
|
+
height: 0
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
this.browser = browser;
|
|
23
|
+
asyncExitHook(() => this.disconnect(), {
|
|
24
|
+
wait: 1e3
|
|
25
|
+
});
|
|
26
|
+
return browser;
|
|
27
|
+
}
|
|
28
|
+
async openBrowser(options = {}) {
|
|
29
|
+
const browser = await puppeteer.launch({
|
|
30
|
+
headless: options.headful ? false : true,
|
|
31
|
+
defaultViewport: {
|
|
32
|
+
width: 1440,
|
|
33
|
+
height: 900
|
|
34
|
+
},
|
|
35
|
+
userDataDir: options.dataDir,
|
|
36
|
+
args: options.proxy ? [`--proxy-server=${options.proxy}`] : void 0
|
|
37
|
+
});
|
|
38
|
+
this.browser = browser;
|
|
39
|
+
asyncExitHook(() => this.close(), {
|
|
40
|
+
wait: 1e3
|
|
41
|
+
});
|
|
42
|
+
return browser;
|
|
43
|
+
}
|
|
44
|
+
async openPage(urlOrHtml, options = {}) {
|
|
45
|
+
const isUrl = /^https?:\/\//i.test(urlOrHtml);
|
|
46
|
+
const pages = await this.browser.pages();
|
|
47
|
+
if (isUrl) {
|
|
48
|
+
const inputUrl = new URL(urlOrHtml);
|
|
49
|
+
const foundPage = pages.find((item) => {
|
|
50
|
+
const pageUrl = item.url();
|
|
51
|
+
const url = new URL(pageUrl);
|
|
52
|
+
return inputUrl.origin === url.origin && (pageUrl === urlOrHtml || !!options.match && (typeof options.match === "string" ? `${url.pathname}${url.search}`.includes(options.match) : options.match.test(`${url.pathname}${url.search}`)));
|
|
53
|
+
});
|
|
54
|
+
if (foundPage) {
|
|
55
|
+
return foundPage;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const page2 = await this.browser.newPage();
|
|
59
|
+
await page2.setUserAgent(
|
|
60
|
+
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36"
|
|
61
|
+
);
|
|
62
|
+
if (options.blockAssets) {
|
|
63
|
+
await page2.setRequestInterception(true);
|
|
64
|
+
page2.on("request", (req) => {
|
|
65
|
+
const resourceType = req.resourceType();
|
|
66
|
+
if (resourceType === "stylesheet" || resourceType === "font" || resourceType === "image") {
|
|
67
|
+
req.abort();
|
|
68
|
+
} else {
|
|
69
|
+
req.continue();
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
if (isUrl) {
|
|
74
|
+
await page2.goto(urlOrHtml, {
|
|
75
|
+
referer: options.referer || urlOrHtml
|
|
76
|
+
});
|
|
77
|
+
} else {
|
|
78
|
+
await page2.setContent(urlOrHtml);
|
|
79
|
+
}
|
|
80
|
+
return page2;
|
|
81
|
+
}
|
|
82
|
+
async input(page2, selector, textOrHtml, frameIndex) {
|
|
83
|
+
const mainPage = page2;
|
|
84
|
+
const keyboard = page2.keyboard;
|
|
85
|
+
if (frameIndex != null) {
|
|
86
|
+
page2 = page2.frames()[frameIndex];
|
|
87
|
+
}
|
|
88
|
+
const el2 = await page2.$(selector);
|
|
89
|
+
const isText = await el2.evaluate((el3) => {
|
|
90
|
+
const isInput = el3.tagName === "INPUT" || el3.tagName === "TEXTAREA";
|
|
91
|
+
const isText2 = isInput || el3.tagName === "SPAN";
|
|
92
|
+
return isText2;
|
|
93
|
+
});
|
|
94
|
+
if (isText) {
|
|
95
|
+
await XClipboard.writeText(textOrHtml);
|
|
96
|
+
} else {
|
|
97
|
+
await XClipboard.writeHtml(textOrHtml);
|
|
98
|
+
}
|
|
99
|
+
await el2.click();
|
|
100
|
+
await this.selectAll(page2);
|
|
101
|
+
await keyboard.press("Backspace");
|
|
102
|
+
await this.paste(mainPage);
|
|
103
|
+
}
|
|
104
|
+
async selectAll(page2) {
|
|
105
|
+
await page2.evaluate(() => {
|
|
106
|
+
const isMac = /Mac|iPod|iPhone|iPad/.test(window.navigator.platform);
|
|
107
|
+
document.activeElement.dispatchEvent(
|
|
108
|
+
new KeyboardEvent("keydown", {
|
|
109
|
+
bubbles: true,
|
|
110
|
+
cancelable: true,
|
|
111
|
+
key: isMac ? "Meta" : "Control",
|
|
112
|
+
code: isMac ? "MetaLeft" : "ControlLeft",
|
|
113
|
+
location: window.KeyboardEvent.DOM_KEY_LOCATION_LEFT,
|
|
114
|
+
// @ts-ignore
|
|
115
|
+
getModifierState: (keyArg) => keyArg === (isMac ? "Meta" : "Control"),
|
|
116
|
+
ctrlKey: !isMac,
|
|
117
|
+
metaKey: isMac,
|
|
118
|
+
charCode: 0,
|
|
119
|
+
keyCode: isMac ? 93 : 17,
|
|
120
|
+
which: isMac ? 93 : 17
|
|
121
|
+
})
|
|
122
|
+
);
|
|
123
|
+
const preventableEvent = new KeyboardEvent("keydown", {
|
|
124
|
+
bubbles: true,
|
|
125
|
+
cancelable: true,
|
|
126
|
+
key: "a",
|
|
127
|
+
code: "KeyA",
|
|
128
|
+
location: window.KeyboardEvent.DOM_KEY_LOCATION_STANDARD,
|
|
129
|
+
// @ts-ignore
|
|
130
|
+
getModifierState: (keyArg) => keyArg === (isMac ? "Meta" : "Control"),
|
|
131
|
+
ctrlKey: !isMac,
|
|
132
|
+
metaKey: isMac,
|
|
133
|
+
charCode: 0,
|
|
134
|
+
keyCode: 65,
|
|
135
|
+
which: 65
|
|
136
|
+
});
|
|
137
|
+
const wasPrevented = !document.activeElement.dispatchEvent(preventableEvent) || preventableEvent.defaultPrevented;
|
|
138
|
+
if (!wasPrevented) {
|
|
139
|
+
document.execCommand("selectall", false, null);
|
|
140
|
+
}
|
|
141
|
+
document.activeElement.dispatchEvent(
|
|
142
|
+
new KeyboardEvent("keyup", {
|
|
143
|
+
bubbles: true,
|
|
144
|
+
cancelable: true,
|
|
145
|
+
key: isMac ? "Meta" : "Control",
|
|
146
|
+
code: isMac ? "MetaLeft" : "ControlLeft",
|
|
147
|
+
location: window.KeyboardEvent.DOM_KEY_LOCATION_LEFT,
|
|
148
|
+
// @ts-ignore
|
|
149
|
+
getModifierState: () => false,
|
|
150
|
+
charCode: 0,
|
|
151
|
+
keyCode: isMac ? 93 : 17,
|
|
152
|
+
which: isMac ? 93 : 17
|
|
153
|
+
})
|
|
154
|
+
);
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
async paste(page2) {
|
|
158
|
+
await page2.keyboard.down("Shift");
|
|
159
|
+
await page2.keyboard.press("Insert");
|
|
160
|
+
await page2.keyboard.up("Shift");
|
|
161
|
+
}
|
|
162
|
+
async queryByText(page, text) {
|
|
163
|
+
const el = await page.evaluateHandle(
|
|
164
|
+
(text, isRegExp, isFunction) => {
|
|
165
|
+
const textRe = isRegExp ? (() => {
|
|
166
|
+
const m = text.match(/^\/(.+)\/([^/]*)$/);
|
|
167
|
+
return new RegExp(m[1], m[2] || void 0);
|
|
168
|
+
})() : void 0;
|
|
169
|
+
const textFn = isFunction ? eval(`(() => {return ${text}})()`) : void 0;
|
|
170
|
+
const walker = document.createTreeWalker(
|
|
171
|
+
document.body,
|
|
172
|
+
NodeFilter.SHOW_TEXT,
|
|
173
|
+
{
|
|
174
|
+
acceptNode: (node) => {
|
|
175
|
+
return (isRegExp ? textRe.test(node.textContent) : isFunction ? textFn(node) : node.textContent.includes(text)) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
);
|
|
179
|
+
walker.nextNode();
|
|
180
|
+
return walker.currentNode.parentElement;
|
|
181
|
+
},
|
|
182
|
+
String(text),
|
|
183
|
+
isRegExp(text),
|
|
184
|
+
isFunction(text)
|
|
185
|
+
);
|
|
186
|
+
return el;
|
|
187
|
+
}
|
|
188
|
+
async disconnect() {
|
|
189
|
+
await this.browser.disconnect();
|
|
190
|
+
}
|
|
191
|
+
async close() {
|
|
192
|
+
await this.browser.close();
|
|
193
|
+
}
|
|
194
|
+
static async openBrowser(options = {}) {
|
|
195
|
+
const xp = new XPuppeteer();
|
|
196
|
+
await xp.openBrowser(options);
|
|
197
|
+
return xp;
|
|
198
|
+
}
|
|
199
|
+
static async openPage(urlOrHtml, options = {}) {
|
|
200
|
+
const xp = new XPuppeteer();
|
|
201
|
+
await xp.openBrowser(options);
|
|
202
|
+
const page2 = await xp.openPage(urlOrHtml, options);
|
|
203
|
+
page2.close = () => xp.close();
|
|
204
|
+
return page2;
|
|
205
|
+
}
|
|
206
|
+
static getPageHtmlXp = memoize(
|
|
207
|
+
async (options) => {
|
|
208
|
+
const xp = new XPuppeteer();
|
|
209
|
+
await (options.useSystemBrowser ? xp.connectSystemBrowser() : xp.openBrowser(options));
|
|
210
|
+
return xp;
|
|
211
|
+
},
|
|
212
|
+
(options) => options.instanceId
|
|
213
|
+
);
|
|
214
|
+
static async getPageHtml(url, options = {}) {
|
|
215
|
+
const hasInstanceId = !!options.instanceId;
|
|
216
|
+
options.instanceId ||= Date.now();
|
|
217
|
+
const xp = await XPuppeteer.getPageHtmlXp(options);
|
|
218
|
+
try {
|
|
219
|
+
const page2 = await xp.openPage(url, options);
|
|
220
|
+
if (options.afterOpenPage) {
|
|
221
|
+
await options.afterOpenPage({
|
|
222
|
+
browser: xp,
|
|
223
|
+
page: page2
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
const html = await page2.content();
|
|
227
|
+
return html;
|
|
228
|
+
} finally {
|
|
229
|
+
if (!hasInstanceId) {
|
|
230
|
+
if (options.useSystemBrowser) {
|
|
231
|
+
await xp.disconnect();
|
|
232
|
+
} else {
|
|
233
|
+
await xp.close();
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
export {
|
|
240
|
+
XPuppeteer
|
|
241
|
+
};
|
package/runner.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { DistinctQuestion } from 'inquirer';
|
|
2
|
+
import { MsValue } from 'vtils';
|
|
3
|
+
export type XRunnerOptions<TConfig extends Record<any, any>> = {
|
|
4
|
+
configFile: string;
|
|
5
|
+
delay: MsValue | ((retryCount: number) => MsValue);
|
|
6
|
+
questions: Array<DistinctQuestion<TConfig>>;
|
|
7
|
+
run: (config: TConfig) => any;
|
|
8
|
+
};
|
|
9
|
+
export declare class XRunner<TConfig extends Record<any, any>> {
|
|
10
|
+
private options;
|
|
11
|
+
constructor(options: XRunnerOptions<TConfig>);
|
|
12
|
+
run(): Promise<void>;
|
|
13
|
+
}
|
package/runner.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import inquirer from "inquirer";
|
|
2
|
+
import { ms, wait } from "vtils";
|
|
3
|
+
import { XFs } from "./fs.js";
|
|
4
|
+
class XRunner {
|
|
5
|
+
constructor(options) {
|
|
6
|
+
this.options = options;
|
|
7
|
+
}
|
|
8
|
+
async run() {
|
|
9
|
+
await XFs.ensureFile(this.options.configFile);
|
|
10
|
+
const savedConfig = await XFs.readJson(this.options.configFile, { throws: false }) || {};
|
|
11
|
+
const config = await inquirer.prompt(
|
|
12
|
+
this.options.questions.map((item) => {
|
|
13
|
+
return {
|
|
14
|
+
...item,
|
|
15
|
+
default: savedConfig[item.name] ?? item.default
|
|
16
|
+
};
|
|
17
|
+
})
|
|
18
|
+
);
|
|
19
|
+
await XFs.writeJson(this.options.configFile, config, { spaces: 2 });
|
|
20
|
+
let retryCount = 0;
|
|
21
|
+
while (true) {
|
|
22
|
+
try {
|
|
23
|
+
await this.options.run(config);
|
|
24
|
+
retryCount = 0;
|
|
25
|
+
} catch (err) {
|
|
26
|
+
const delay = typeof this.options.delay === "function" ? this.options.delay(retryCount) : this.options.delay;
|
|
27
|
+
console.log(err);
|
|
28
|
+
console.log(`\u6267\u884C\u62A5\u9519, \u7B49\u5F85${delay}`);
|
|
29
|
+
await wait(ms(delay));
|
|
30
|
+
retryCount++;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
new XRunner({
|
|
36
|
+
configFile: "./temp/test.json",
|
|
37
|
+
delay: "1s",
|
|
38
|
+
questions: [
|
|
39
|
+
{
|
|
40
|
+
name: "perCount",
|
|
41
|
+
message: "\u6BCF\u8F6E\u5904\u7406\u6761\u6570",
|
|
42
|
+
type: "number",
|
|
43
|
+
default: 500
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
name: "concurrencyCount",
|
|
47
|
+
message: "\u6BCF\u8F6E\u5E76\u884C\u5904\u7406\u6761\u6570",
|
|
48
|
+
type: "number",
|
|
49
|
+
default: 1
|
|
50
|
+
}
|
|
51
|
+
],
|
|
52
|
+
run: (config) => {
|
|
53
|
+
console.log(config);
|
|
54
|
+
}
|
|
55
|
+
}).run();
|
|
56
|
+
export {
|
|
57
|
+
XRunner
|
|
58
|
+
};
|
package/task.d.ts
ADDED
package/task.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { consola as log } from "consola";
|
|
2
|
+
import { asyncExitHook, gracefulExit } from "exit-hook";
|
|
3
|
+
class XTask {
|
|
4
|
+
log = log;
|
|
5
|
+
async dispose() {
|
|
6
|
+
}
|
|
7
|
+
run() {
|
|
8
|
+
asyncExitHook(() => this.dispose(), {
|
|
9
|
+
wait: 3e3
|
|
10
|
+
});
|
|
11
|
+
return this.start().then(() => {
|
|
12
|
+
gracefulExit(0);
|
|
13
|
+
}).catch((err) => {
|
|
14
|
+
log.error(err);
|
|
15
|
+
gracefulExit(1);
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export {
|
|
20
|
+
XTask
|
|
21
|
+
};
|
package/text.d.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
export declare class XText {
|
|
2
|
+
private text;
|
|
3
|
+
/**
|
|
4
|
+
* 将字符串分割为字符数组。
|
|
5
|
+
*
|
|
6
|
+
* @see https://stackoverflow.com/a/71619350
|
|
7
|
+
*/
|
|
8
|
+
static split(text: string): string[];
|
|
9
|
+
/**
|
|
10
|
+
* 纯粹为了书写 HTML 有提示。
|
|
11
|
+
*/
|
|
12
|
+
static html(literals: any, ...interpolations: any): string;
|
|
13
|
+
/**
|
|
14
|
+
* 切割字符串。
|
|
15
|
+
*/
|
|
16
|
+
static cut(text: string, start: string, end: string): string;
|
|
17
|
+
/**
|
|
18
|
+
* 移除全部空白字符。
|
|
19
|
+
*/
|
|
20
|
+
static stripWhitespace(text: string): string;
|
|
21
|
+
/**
|
|
22
|
+
* 移除首尾空白字符。
|
|
23
|
+
*/
|
|
24
|
+
static stripMarginWhitespace(text: string): string;
|
|
25
|
+
/**
|
|
26
|
+
* HTML 实体编码。
|
|
27
|
+
*/
|
|
28
|
+
static encodeHtmlEntity(text: string): string;
|
|
29
|
+
/**
|
|
30
|
+
* HTML 实体解码。
|
|
31
|
+
*/
|
|
32
|
+
static decodeHtmlEntity(text: string): string;
|
|
33
|
+
/**
|
|
34
|
+
* 移除 HTML 代码。
|
|
35
|
+
*/
|
|
36
|
+
static stripHtml(text: string): string;
|
|
37
|
+
constructor(text: string);
|
|
38
|
+
/**
|
|
39
|
+
* 切割字符串。
|
|
40
|
+
*/
|
|
41
|
+
cut(start: string, end: string): this;
|
|
42
|
+
/**
|
|
43
|
+
* 移除全部空白字符。
|
|
44
|
+
*/
|
|
45
|
+
stripWhitespace(): this;
|
|
46
|
+
/**
|
|
47
|
+
* 移除首尾空白字符。
|
|
48
|
+
*/
|
|
49
|
+
stripMarginWhitespace(): this;
|
|
50
|
+
/**
|
|
51
|
+
* HTML 实体编码。
|
|
52
|
+
*/
|
|
53
|
+
encodeHtmlEntity(): this;
|
|
54
|
+
/**
|
|
55
|
+
* HTML 实体解码。
|
|
56
|
+
*/
|
|
57
|
+
decodeHtmlEntity(): this;
|
|
58
|
+
/**
|
|
59
|
+
* 移除 HTML 代码。
|
|
60
|
+
*/
|
|
61
|
+
stripHtml(): this;
|
|
62
|
+
toString(): string;
|
|
63
|
+
}
|