single-file-cli 2.0.22 → 2.0.24
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 +1 -1
- package/deno.json +1 -1
- package/lib/cdp-client.js +24 -18
- package/lib/deno-polyfill.js +0 -10
- package/lib/version.js +1 -1
- package/package.json +1 -1
- package/single-file-cli-api.js +23 -16
- package/single-file-launcher.js +2 -8
- package/single-file.bat +1 -1
package/README.MD
CHANGED
|
@@ -69,7 +69,7 @@ Make sure Chrome or a Chromium-based browser is installed in the default folder.
|
|
|
69
69
|
|
|
70
70
|
```sh
|
|
71
71
|
npm install "single-file-cli"
|
|
72
|
-
cd single-file-cli
|
|
72
|
+
cd node_modules/single-file-cli
|
|
73
73
|
```
|
|
74
74
|
|
|
75
75
|
You can also install SingleFile globally with `-g` when running `npm install`.
|
package/deno.json
CHANGED
package/lib/cdp-client.js
CHANGED
|
@@ -28,29 +28,30 @@ import { getScriptSource, getHookScriptSource } from "./single-file-script.js";
|
|
|
28
28
|
import { CDP } from "simple-cdp";
|
|
29
29
|
|
|
30
30
|
const LOAD_TIMEOUT_ERROR = "ERR_LOAD_TIMEOUT";
|
|
31
|
-
const
|
|
31
|
+
const DEFAULT_NETWORK_STATE = "networkIdle";
|
|
32
32
|
const NETWORK_STATES = ["InteractiveTime", "networkIdle", "networkAlmostIdle", "load", "DOMContentLoaded"];
|
|
33
33
|
const MINIMIZED_WINDOW_STATE = "minimized";
|
|
34
34
|
const SINGLE_FILE_WORLD_NAME = "singlefile";
|
|
35
35
|
const EMPTY_PAGE_URL = "about:blank";
|
|
36
36
|
|
|
37
|
-
export { initialize, getPageData, closeBrowser };
|
|
37
|
+
export { initialize, getPageData, hasTargets, closeBrowser };
|
|
38
38
|
|
|
39
39
|
async function initialize(options) {
|
|
40
|
-
if (
|
|
41
|
-
await launchBrowser(getBrowserOptions(options));
|
|
42
|
-
} else {
|
|
40
|
+
if (options.browserRemoteDebuggingURL) {
|
|
43
41
|
CDP.remoteDebuggingUrl = options.browserRemoteDebuggingURL;
|
|
42
|
+
} else {
|
|
43
|
+
await launchBrowser(getBrowserOptions(options));
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
async function getPageData(options) {
|
|
48
|
+
let Browser, Security, Page, Emulation, Fetch, Network, Runtime, Debugger, Target;
|
|
48
49
|
let targetInfo;
|
|
49
50
|
try {
|
|
50
51
|
targetInfo = await CDP.createTarget(EMPTY_PAGE_URL);
|
|
51
|
-
|
|
52
|
+
({ Browser, Security, Page, Emulation, Fetch, Network, Runtime, Debugger, Target } = new CDP(targetInfo));
|
|
52
53
|
if (options.browserStartMinimized) {
|
|
53
|
-
const { windowId, bounds } = await Browser.getWindowForTarget({ targetId: targetInfo.
|
|
54
|
+
const { windowId, bounds } = await Browser.getWindowForTarget({ targetId: targetInfo.id });
|
|
54
55
|
if (bounds.windowState !== MINIMIZED_WINDOW_STATE) {
|
|
55
56
|
await Browser.setWindowBounds({ windowId, bounds: { windowState: MINIMIZED_WINDOW_STATE } });
|
|
56
57
|
}
|
|
@@ -130,12 +131,17 @@ async function getPageData(options) {
|
|
|
130
131
|
}
|
|
131
132
|
throw error;
|
|
132
133
|
} finally {
|
|
133
|
-
if (targetInfo
|
|
134
|
+
if (targetInfo) {
|
|
134
135
|
await CDP.closeTarget(targetInfo.id);
|
|
135
136
|
}
|
|
136
137
|
}
|
|
137
138
|
}
|
|
138
139
|
|
|
140
|
+
async function hasTargets() {
|
|
141
|
+
const targets = await CDP.getTargets();
|
|
142
|
+
return targets.filter(target => target.type === "page").length > 0;
|
|
143
|
+
}
|
|
144
|
+
|
|
139
145
|
async function loadPage({ Page, Runtime }, options) {
|
|
140
146
|
await Runtime.enable();
|
|
141
147
|
await Page.enable();
|
|
@@ -194,12 +200,14 @@ function getTopFrameContextId({ Page, Runtime }, options) {
|
|
|
194
200
|
contextId = contextIds[contextIdIndex];
|
|
195
201
|
}
|
|
196
202
|
contextIdIndex++;
|
|
197
|
-
} while (contextId === undefined && contextIdIndex < contextIds.length);
|
|
203
|
+
} while (contextId === undefined && contextIdIndex < contextIds.length && !navigationSignal.aborted);
|
|
198
204
|
}
|
|
199
|
-
if (
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
205
|
+
if (!navigationSignal.aborted) {
|
|
206
|
+
if (contextId === undefined) {
|
|
207
|
+
reject(new Error("Execution context not found"));
|
|
208
|
+
} else {
|
|
209
|
+
resolve(contextId);
|
|
210
|
+
}
|
|
203
211
|
}
|
|
204
212
|
}
|
|
205
213
|
}
|
|
@@ -262,11 +270,9 @@ async function waitForPageReady({ Page }, topFrameId, options, navigationSignal)
|
|
|
262
270
|
|
|
263
271
|
function onLifecycleEvent({ params }) {
|
|
264
272
|
const { name, frameId } = params;
|
|
265
|
-
if (frameId === topFrameId) {
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
resolve();
|
|
269
|
-
}
|
|
273
|
+
if (frameId === topFrameId && (name === options.browserWaitUntil || name === DEFAULT_NETWORK_STATE || navigationSignal.aborted)) {
|
|
274
|
+
Page.removeEventListener(LIFE_CYCLE_EVENT, onLifecycleEvent);
|
|
275
|
+
resolve();
|
|
270
276
|
}
|
|
271
277
|
}
|
|
272
278
|
});
|
package/lib/deno-polyfill.js
CHANGED
|
@@ -114,7 +114,6 @@ const DenoAPI = {
|
|
|
114
114
|
};
|
|
115
115
|
|
|
116
116
|
const pathAPI = {
|
|
117
|
-
toFileUrl,
|
|
118
117
|
dirname
|
|
119
118
|
};
|
|
120
119
|
|
|
@@ -208,15 +207,6 @@ function exit(code) {
|
|
|
208
207
|
}
|
|
209
208
|
}
|
|
210
209
|
|
|
211
|
-
async function toFileUrl(filePath) {
|
|
212
|
-
if (DENO_RUNTIME_DETECTED) {
|
|
213
|
-
return path.toFileUrl(filePath);
|
|
214
|
-
} else {
|
|
215
|
-
const url = await import(NODE_MODULES["url"]);
|
|
216
|
-
return new URL(url.pathToFileURL(filePath));
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
210
|
async function dirname(filePath) {
|
|
221
211
|
return path.dirname(filePath);
|
|
222
212
|
}
|
package/lib/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = "2.0.
|
|
1
|
+
export const version = "2.0.24";
|
package/package.json
CHANGED
package/single-file-cli-api.js
CHANGED
|
@@ -79,7 +79,7 @@ const STATE_PROCESSED = "processed";
|
|
|
79
79
|
const { readTextFile, writeTextFile, writeFile, stdout, mkdir, stat, errors } = Deno;
|
|
80
80
|
let tasks = [], maxParallelWorkers, sessionFilename;
|
|
81
81
|
|
|
82
|
-
export {
|
|
82
|
+
export { initialize };
|
|
83
83
|
|
|
84
84
|
async function initialize(options) {
|
|
85
85
|
options = Object.assign({}, DEFAULT_OPTIONS, options);
|
|
@@ -151,7 +151,7 @@ async function finish(options) {
|
|
|
151
151
|
}
|
|
152
152
|
}
|
|
153
153
|
}
|
|
154
|
-
if (!options.browserDebug) {
|
|
154
|
+
if (!options.browserDebug && !options.browserRemoteDebuggingURL && !(await backend.hasTargets())) {
|
|
155
155
|
return backend.closeBrowser();
|
|
156
156
|
}
|
|
157
157
|
}
|
|
@@ -203,21 +203,28 @@ function testMaxDepth(task) {
|
|
|
203
203
|
|
|
204
204
|
function createTask(url, options, parentTask, rootTask) {
|
|
205
205
|
url = parentTask ? rewriteURL(url, options.crawlRemoveURLFragment, options.crawlRewriteRules) : url;
|
|
206
|
-
if (VALID_URL_TEST.test(url)) {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
originalUrl: url,
|
|
215
|
-
rootBaseURI: rootBaseURIMatch && rootBaseURIMatch[1],
|
|
216
|
-
depth: parentTask ? parentTask.depth + 1 : 0,
|
|
217
|
-
externalLinkDepth: isInnerLink ? -1 : parentTask ? parentTask.externalLinkDepth + 1 : -1,
|
|
218
|
-
options
|
|
219
|
-
};
|
|
206
|
+
if (!VALID_URL_TEST.test(url)) {
|
|
207
|
+
try {
|
|
208
|
+
url = url.replace(/\\/g, "/");
|
|
209
|
+
url = url.replace(/#/g, "%23");
|
|
210
|
+
url = new URL(url, import.meta.url).href;
|
|
211
|
+
} catch (error) {
|
|
212
|
+
throw new Error("Invalid URL or file path: " + url);
|
|
213
|
+
}
|
|
220
214
|
}
|
|
215
|
+
const isInnerLink = rootTask && url.startsWith(getHostURL(rootTask.url));
|
|
216
|
+
const rootBaseURIMatch = rootTask && rootTask.url.match(/(.*?)[^/]*$/);
|
|
217
|
+
const isChild = isInnerLink && rootBaseURIMatch && rootBaseURIMatch[1] && url.startsWith(rootBaseURIMatch[1]);
|
|
218
|
+
return {
|
|
219
|
+
url,
|
|
220
|
+
isInnerLink,
|
|
221
|
+
isChild,
|
|
222
|
+
originalUrl: url,
|
|
223
|
+
rootBaseURI: rootBaseURIMatch && rootBaseURIMatch[1],
|
|
224
|
+
depth: parentTask ? parentTask.depth + 1 : 0,
|
|
225
|
+
externalLinkDepth: isInnerLink ? -1 : parentTask ? parentTask.externalLinkDepth + 1 : -1,
|
|
226
|
+
options
|
|
227
|
+
};
|
|
221
228
|
}
|
|
222
229
|
|
|
223
230
|
async function saveTasks() {
|
package/single-file-launcher.js
CHANGED
|
@@ -21,23 +21,17 @@
|
|
|
21
21
|
* Source.
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
import { VALID_URL_TEST, initialize } from "./single-file-cli-api.js";
|
|
27
|
-
import { Deno, path } from "./lib/deno-polyfill.js";
|
|
24
|
+
import { initialize } from "./single-file-cli-api.js";
|
|
25
|
+
import { Deno } from "./lib/deno-polyfill.js";
|
|
28
26
|
import options from "./options.js";
|
|
29
27
|
|
|
30
28
|
const { readTextFile, exit } = Deno;
|
|
31
|
-
const { toFileUrl } = path;
|
|
32
29
|
|
|
33
30
|
export { run };
|
|
34
31
|
|
|
35
32
|
async function run() {
|
|
36
33
|
try {
|
|
37
34
|
let urls;
|
|
38
|
-
if (options.url && !VALID_URL_TEST.test(options.url)) {
|
|
39
|
-
options.url = (await toFileUrl(new URL(options.url, import.meta.url).pathname)).href;
|
|
40
|
-
}
|
|
41
35
|
if (options.urlsFile) {
|
|
42
36
|
urls = (await readTextFile(options.urlsFile)).split("\n");
|
|
43
37
|
} else {
|
package/single-file.bat
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
@echo off
|
|
2
|
-
|
|
2
|
+
deno run --allow-read --allow-write --allow-net --allow-env --allow-run "%~dp0\single-file" %*
|