single-file-cli 2.0.68 → 2.0.70
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/build.sh +8 -3
- package/deno.json +2 -2
- package/lib/browser.js +2 -2
- package/lib/cdp-client.js +140 -25
- package/lib/constants.js +3 -1
- package/lib/deno-polyfill.js +3 -1
- package/lib/single-file-bundle.js +1 -1
- package/lib/single-file-script.js +1 -1
- package/lib/version.js +1 -1
- package/options.js +86 -43
- package/package.json +1 -1
- package/single-file-cli-api.js +30 -7
- package/single-file-launcher.js +56 -3
|
@@ -41,7 +41,7 @@ function initSingleFile() {
|
|
|
41
41
|
xhrRequest.onreadystatechange = () => {
|
|
42
42
|
if (xhrRequest.readyState == XMLHttpRequest.DONE) {
|
|
43
43
|
resolve({
|
|
44
|
-
arrayBuffer:
|
|
44
|
+
arrayBuffer: () => xhrRequest.response || new ArrayBuffer(),
|
|
45
45
|
headers: { get: headerName => xhrRequest.getResponseHeader(headerName) },
|
|
46
46
|
status: xhrRequest.status
|
|
47
47
|
});
|
package/lib/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = "2.0.
|
|
1
|
+
export const version = "2.0.70";
|
package/options.js
CHANGED
|
@@ -42,6 +42,7 @@ const OPTIONS_INFO = {
|
|
|
42
42
|
"block-scripts": { description: "Block scripts", type: "boolean", defaultValue: true },
|
|
43
43
|
"block-videos": { description: "Block videos", type: "boolean", defaultValue: true },
|
|
44
44
|
"block-mixed-content": { description: "Block mixed contents", type: "boolean" },
|
|
45
|
+
"blocked-URL-pattern": { description: "Regular expression matching URLs to block (e.g. 'annoying-banners\\.com')", type: "string[]" },
|
|
45
46
|
"browser-server": { description: "Server to connect to", type: "string" },
|
|
46
47
|
"browser-headless": { description: "Run the browser in headless mode", type: "boolean", defaultValue: true },
|
|
47
48
|
"browser-executable-path": { description: "Path to chrome/chromium executable", type: "string" },
|
|
@@ -50,6 +51,7 @@ const OPTIONS_INFO = {
|
|
|
50
51
|
"browser-load-max-time": { description: "Maximum delay of time to wait for page loading in ms", type: "number", defaultValue: 60000 },
|
|
51
52
|
"browser-wait-delay": { description: "Time to wait before capturing the page in ms", type: "number" },
|
|
52
53
|
"browser-wait-until": { description: "When to consider the page is loaded (InteractiveTime, networkIdle, networkAlmostIdle, load, domContentLoaded)", type: "string", defaultValue: "networkIdle" },
|
|
54
|
+
"browser-wait-until-delay": { description: "Delay of time in ms to wait before considering the page is loaded when the value of --browser-wait-until is reached", type: "number", defaultValue: 1000 },
|
|
53
55
|
"browser-wait-until-fallback": { description: "Retry with the next value of --browser-wait-until when a timeout error is thrown", type: "boolean", defaultValue: true },
|
|
54
56
|
"browser-debug": { description: "Enable debug mode", type: "boolean" },
|
|
55
57
|
"browser-script": { description: "Path of a script executed in the page (and all the frames) before it is loaded", type: "string[]" },
|
|
@@ -66,6 +68,7 @@ const OPTIONS_INFO = {
|
|
|
66
68
|
"browser-device-height": { description: "Height of the device viewport in pixels (default value is 800 when using --browser-mobile-emulation)", type: "number" },
|
|
67
69
|
"browser-device-scale-factor": { description: "Scale factor of the device viewport (default value is 2 when using --browser-mobile-emulation)", type: "number" },
|
|
68
70
|
"console-messages-file": { description: "Path of the file where to save the console messages in JSON format", type: "string" },
|
|
71
|
+
"debug-messages-file": { description: "Path of the file where to save the debug messages", type: "string" },
|
|
69
72
|
"compress-CSS": { description: "Compress CSS stylesheets", type: "boolean" },
|
|
70
73
|
"compress-HTML": { description: "Compress HTML content", type: "boolean", defaultValue: true },
|
|
71
74
|
"crawl-links": { description: "Crawl and save pages found via inner links", type: "boolean" },
|
|
@@ -121,7 +124,7 @@ const OPTIONS_INFO = {
|
|
|
121
124
|
"remove-alternative-images": { description: "Remove images for alternative sizes of screen", type: "boolean", defaultValue: true },
|
|
122
125
|
"save-original-URLs": { description: "Save the original URLS in the embedded contents", type: "boolean" },
|
|
123
126
|
"save-raw-page": { description: "Save the original page without interpreting it into the browser", type: "boolean" },
|
|
124
|
-
"urls-file": { description: "Path to a text file containing a list of URLs (separated by a newline) to save", type: "string" },
|
|
127
|
+
"urls-file": { description: "Path to a text file containing a list of URLs (separated by a newline) to save. You can also pass the options after each URL (e.g. 'https://www.example.com --filename-template={page-title}.html')", type: "string" },
|
|
125
128
|
"user-agent": { description: "User-agent of the browser", type: "string" },
|
|
126
129
|
"accept-language": { description: "Accept language of the browser", type: "string" },
|
|
127
130
|
"platform": { description: "Platform of the browser (default value is \"Android\" when using --browser-mobile-emulation)", type: "string" },
|
|
@@ -145,14 +148,14 @@ const OPTIONS_INFO = {
|
|
|
145
148
|
"resolve-links": { description: "Resolve link URLs to absolute URLs", type: "boolean", defaultValue: true },
|
|
146
149
|
"settings-file": { description: "Path to a JSON file containing the settings exported from the web extension", type: "string" },
|
|
147
150
|
"settings-file-profile": { description: "Name of the profile to use when using --settings-file", type: "string", defaultValue: "default" },
|
|
148
|
-
"group-duplicate-stylesheets": { description: "Group duplicate inline stylesheets into a single stylesheet in order to reduce the size of the page", type: "boolean", defaultValue:
|
|
151
|
+
"group-duplicate-stylesheets": { description: "Group duplicate inline stylesheets into a single stylesheet in order to reduce the size of the page", type: "boolean", defaultValue: false }
|
|
149
152
|
};
|
|
150
153
|
|
|
151
154
|
const { args, exit } = Deno;
|
|
152
|
-
const options =
|
|
153
|
-
export
|
|
155
|
+
const options = getOptions();
|
|
156
|
+
export { options, parseArgs };
|
|
154
157
|
|
|
155
|
-
|
|
158
|
+
function getOptions() {
|
|
156
159
|
const { positionals, options } = parseArgs(Array.from(args));
|
|
157
160
|
const unknownOptions = [];
|
|
158
161
|
positionals.forEach(positional => {
|
|
@@ -185,47 +188,10 @@ async function getOptions() {
|
|
|
185
188
|
console.log(version); // eslint-disable-line no-console
|
|
186
189
|
exit(0);
|
|
187
190
|
}
|
|
188
|
-
Object.keys(OPTIONS_INFO).forEach(optionName => {
|
|
189
|
-
const optionInfo = getOptionInfo(optionName);
|
|
190
|
-
const optionKey = getOptionKey(optionName, optionInfo);
|
|
191
|
-
if (options[optionKey] === undefined && optionInfo.defaultValue !== undefined) {
|
|
192
|
-
options[optionKey] = OPTIONS_INFO[optionName].defaultValue;
|
|
193
|
-
}
|
|
194
|
-
});
|
|
195
|
-
options.acceptHeaders = {
|
|
196
|
-
font: options.acceptHeaderFont,
|
|
197
|
-
image: options.acceptHeaderImage,
|
|
198
|
-
stylesheet: options.acceptHeaderStylesheet,
|
|
199
|
-
script: options.acceptHeaderScript,
|
|
200
|
-
document: options.acceptHeaderDocument
|
|
201
|
-
};
|
|
202
|
-
if (options.browserArgs) {
|
|
203
|
-
const browserArguments = options.browserArguments || [];
|
|
204
|
-
browserArguments.push(...JSON.parse(options.browserArgs));
|
|
205
|
-
options.browserArgs = browserArguments;
|
|
206
|
-
delete options.browserArguments;
|
|
207
|
-
}
|
|
208
|
-
if (options.browserArguments) {
|
|
209
|
-
options.browserArgs = options.browserArguments;
|
|
210
|
-
delete options.browserArguments;
|
|
211
|
-
}
|
|
212
|
-
if (options.errorFile) {
|
|
213
|
-
options.errorsFile = options.errorFile;
|
|
214
|
-
delete options.errorFile;
|
|
215
|
-
}
|
|
216
|
-
if (options.errorTracesDisabled) {
|
|
217
|
-
options.errorsTracesDisabled = options.errorTracesDisabled;
|
|
218
|
-
delete options.errorTracesDisabled;
|
|
219
|
-
}
|
|
220
|
-
delete options.acceptHeaderFont;
|
|
221
|
-
delete options.acceptHeaderImage;
|
|
222
|
-
delete options.acceptHeaderStylesheet;
|
|
223
|
-
delete options.acceptHeaderScript;
|
|
224
|
-
delete options.acceptHeaderDocument;
|
|
225
191
|
return { ...options, url: positionals[0], output: positionals[1] };
|
|
226
192
|
}
|
|
227
193
|
|
|
228
|
-
function parseArgs(args) {
|
|
194
|
+
function parseArgs(args, setDefaultValues = true) {
|
|
229
195
|
const positionals = [];
|
|
230
196
|
const options = {};
|
|
231
197
|
const result = { positionals, options: {} };
|
|
@@ -280,6 +246,83 @@ function parseArgs(args) {
|
|
|
280
246
|
}
|
|
281
247
|
result.options[optionKey] = optionValue;
|
|
282
248
|
});
|
|
249
|
+
if (setDefaultValues) {
|
|
250
|
+
Object.keys(OPTIONS_INFO).forEach(optionName => {
|
|
251
|
+
const optionInfo = getOptionInfo(optionName);
|
|
252
|
+
const optionKey = getOptionKey(optionName, optionInfo);
|
|
253
|
+
if (result.options[optionKey] === undefined && optionInfo.defaultValue !== undefined) {
|
|
254
|
+
result.options[optionKey] = OPTIONS_INFO[optionName].defaultValue;
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
if (result.options.acceptHeaderFont ||
|
|
259
|
+
result.options.acceptHeaderImage ||
|
|
260
|
+
result.options.acceptHeaderStylesheet ||
|
|
261
|
+
result.options.acceptHeaderScript ||
|
|
262
|
+
result.options.acceptHeaderDocument) {
|
|
263
|
+
result.options.acceptHeaders = {
|
|
264
|
+
font: result.options.acceptHeaderFont,
|
|
265
|
+
image: result.options.acceptHeaderImage,
|
|
266
|
+
stylesheet: result.options.acceptHeaderStylesheet,
|
|
267
|
+
script: result.options.acceptHeaderScript,
|
|
268
|
+
document: result.options.acceptHeaderDocument
|
|
269
|
+
};
|
|
270
|
+
delete result.options.acceptHeaderFont;
|
|
271
|
+
delete result.options.acceptHeaderImage;
|
|
272
|
+
delete result.options.acceptHeaderStylesheet;
|
|
273
|
+
delete result.options.acceptHeaderScript;
|
|
274
|
+
delete result.options.acceptHeaderDocument;
|
|
275
|
+
}
|
|
276
|
+
if (result.options.browserArgs) {
|
|
277
|
+
const browserArguments = result.options.browserArguments || [];
|
|
278
|
+
browserArguments.push(...JSON.parse(result.options.browserArgs));
|
|
279
|
+
result.options.browserArgs = browserArguments;
|
|
280
|
+
delete result.options.browserArguments;
|
|
281
|
+
}
|
|
282
|
+
if (result.options.browserArguments) {
|
|
283
|
+
result.options.browserArgs = result.options.browserArguments;
|
|
284
|
+
delete result.options.browserArguments;
|
|
285
|
+
}
|
|
286
|
+
if (result.options.errorFile) {
|
|
287
|
+
result.options.errorsFile = result.options.errorFile;
|
|
288
|
+
delete result.options.errorFile;
|
|
289
|
+
}
|
|
290
|
+
if (result.options.errorTracesDisabled) {
|
|
291
|
+
result.options.errorsTracesDisabled = result.options.errorTracesDisabled;
|
|
292
|
+
delete result.options.errorTracesDisabled;
|
|
293
|
+
}
|
|
294
|
+
if (result.options.crawlReplaceUrls) {
|
|
295
|
+
result.options.crawlReplaceURLs = result.options.crawlReplaceUrls;
|
|
296
|
+
delete result.options.crawlReplaceUrls;
|
|
297
|
+
}
|
|
298
|
+
if (result.options.saveOriginalUrls) {
|
|
299
|
+
result.options.saveOriginalURLs = result.options.saveOriginalUrls;
|
|
300
|
+
delete result.options.saveOriginalUrls;
|
|
301
|
+
}
|
|
302
|
+
if (result.options.browserRemoteDebuggingUrl) {
|
|
303
|
+
result.options.browserRemoteDebuggingURL = result.options.browserRemoteDebuggingUrl;
|
|
304
|
+
delete result.options.browserRemoteDebuggingUrl;
|
|
305
|
+
}
|
|
306
|
+
if (result.options.crawlRemoveUrlFragment) {
|
|
307
|
+
result.options.crawlRemoveURLFragment = result.options.crawlRemoveUrlFragment;
|
|
308
|
+
delete result.options.crawlRemoveUrlFragment;
|
|
309
|
+
}
|
|
310
|
+
if (result.options.compressCss) {
|
|
311
|
+
result.options.compressCSS = result.options.compressCss;
|
|
312
|
+
delete result.options.compressCss;
|
|
313
|
+
}
|
|
314
|
+
if (result.options.compressHtml) {
|
|
315
|
+
result.options.compressHTML = result.options.compressHtml;
|
|
316
|
+
delete result.options.compressHtml;
|
|
317
|
+
}
|
|
318
|
+
if (result.options.insertMetaCsp) {
|
|
319
|
+
result.options.insertMetaCSP = result.options.insertMetaCsp;
|
|
320
|
+
delete result.options.insertMetaCsp;
|
|
321
|
+
}
|
|
322
|
+
if (result.options.blockedUrlPatterns) {
|
|
323
|
+
result.options.blockedURLPatterns = result.options.blockedUrlPatterns;
|
|
324
|
+
delete result.options.blockedUrlPatterns;
|
|
325
|
+
}
|
|
283
326
|
return result;
|
|
284
327
|
}
|
|
285
328
|
|
package/package.json
CHANGED
package/single-file-cli-api.js
CHANGED
|
@@ -39,8 +39,9 @@ const DEFAULT_OPTIONS = {
|
|
|
39
39
|
filenameTemplate: "{page-title} ({date-locale} {time-locale}).html",
|
|
40
40
|
filenameMaxLength: 192,
|
|
41
41
|
filenameMaxLengthUnit: "bytes",
|
|
42
|
-
filenameReplacedCharacters: ["~", "+", "
|
|
42
|
+
filenameReplacedCharacters: ["~", "+", "?", "%", "*", ":", "|", "\"", "<", ">", "\\\\", "\x00-\x1f", "\x7F"],
|
|
43
43
|
filenameReplacementCharacter: "_",
|
|
44
|
+
filenameReplacementCharacters: ["~", "+", "?", "%", "*", ":", "|", """, "<", ">", "\"],
|
|
44
45
|
maxResourceSize: 10,
|
|
45
46
|
backgroundSave: true,
|
|
46
47
|
removeAlternativeFonts: true,
|
|
@@ -101,7 +102,17 @@ async function initialize(options) {
|
|
|
101
102
|
async function capture(urls, options) {
|
|
102
103
|
let newTasks;
|
|
103
104
|
const taskUrls = tasks.map(task => task.url);
|
|
104
|
-
newTasks = await Promise.all(urls.map(
|
|
105
|
+
newTasks = await Promise.all(urls.map(value => {
|
|
106
|
+
let url, taskOptions;
|
|
107
|
+
if (Array.isArray(value)) {
|
|
108
|
+
url = value[0];
|
|
109
|
+
taskOptions = Object.assign({}, options, value[1]);
|
|
110
|
+
} else {
|
|
111
|
+
url = value;
|
|
112
|
+
taskOptions = options;
|
|
113
|
+
}
|
|
114
|
+
return createTask(url, taskOptions);
|
|
115
|
+
}));
|
|
105
116
|
newTasks = newTasks.filter(task => task && !taskUrls.includes(task.url));
|
|
106
117
|
if (newTasks.length) {
|
|
107
118
|
tasks = tasks.concat(newTasks);
|
|
@@ -127,7 +138,7 @@ async function finish(options) {
|
|
|
127
138
|
}
|
|
128
139
|
});
|
|
129
140
|
await writeTextFile(task.filename, pageContent);
|
|
130
|
-
} catch
|
|
141
|
+
} catch {
|
|
131
142
|
// ignored
|
|
132
143
|
}
|
|
133
144
|
}
|
|
@@ -137,7 +148,7 @@ async function finish(options) {
|
|
|
137
148
|
}
|
|
138
149
|
}
|
|
139
150
|
|
|
140
|
-
|
|
151
|
+
function runTasks() {
|
|
141
152
|
const availableTasks = tasks.filter(task => !task.status).length;
|
|
142
153
|
const processingTasks = tasks.filter(task => task.status == STATE_PROCESSING).length;
|
|
143
154
|
const promisesTasks = [];
|
|
@@ -151,7 +162,7 @@ async function runNextTask() {
|
|
|
151
162
|
const task = tasks.find(task => !task.status);
|
|
152
163
|
if (task) {
|
|
153
164
|
const options = task.options;
|
|
154
|
-
|
|
165
|
+
const taskOptions = JSON.parse(JSON.stringify(options));
|
|
155
166
|
taskOptions.url = task.url;
|
|
156
167
|
task.status = STATE_PROCESSING;
|
|
157
168
|
await saveTasks();
|
|
@@ -166,6 +177,7 @@ async function runNextTask() {
|
|
|
166
177
|
newTasks = newTasks.filter(task => task &&
|
|
167
178
|
testMaxDepth(task) &&
|
|
168
179
|
!tasks.find(otherTask => otherTask.url == task.url) &&
|
|
180
|
+
!newTasks.find(otherTask => otherTask != task && otherTask.url == task.url) &&
|
|
169
181
|
(!options.crawlInnerLinksOnly || task.isInnerLink) &&
|
|
170
182
|
(!options.crawlNoParent || (task.isChild || !task.isInnerLink)));
|
|
171
183
|
tasks.splice(tasks.length, 0, ...newTasks);
|
|
@@ -251,6 +263,10 @@ async function capturePage(options) {
|
|
|
251
263
|
if (options.consoleMessagesFile && pageData.consoleMessages) {
|
|
252
264
|
await writeTextFile(options.consoleMessagesFile, JSON.stringify(pageData.consoleMessages, null, 2));
|
|
253
265
|
}
|
|
266
|
+
if (options.debugMessagesFile && pageData.debugMessages) {
|
|
267
|
+
await writeTextFile(options.debugMessagesFile, pageData.debugMessages.map(([timestamp, message]) =>
|
|
268
|
+
`[${new Date(timestamp).toISOString()}] ${message.join(" ")}`).join("\n"));
|
|
269
|
+
}
|
|
254
270
|
if (options.outputJson) {
|
|
255
271
|
if (content instanceof Uint8Array) {
|
|
256
272
|
const fileReader = new FileReader();
|
|
@@ -282,7 +298,7 @@ async function capturePage(options) {
|
|
|
282
298
|
if (options.outputJson) {
|
|
283
299
|
filename += filename.endsWith(".json") ? "" : ".json";
|
|
284
300
|
}
|
|
285
|
-
const directoryName =
|
|
301
|
+
const directoryName = path.dirname(filename);
|
|
286
302
|
if (directoryName !== ".") {
|
|
287
303
|
await mkdir(directoryName, { recursive: true });
|
|
288
304
|
}
|
|
@@ -305,6 +321,13 @@ async function capturePage(options) {
|
|
|
305
321
|
} else {
|
|
306
322
|
console.error(error.message || error, message); // eslint-disable-line no-console
|
|
307
323
|
}
|
|
324
|
+
if (options.consoleMessagesFile && error.consoleMessages) {
|
|
325
|
+
await writeTextFile(options.consoleMessagesFile, JSON.stringify(error.consoleMessages, null, 2));
|
|
326
|
+
}
|
|
327
|
+
if (options.debugMessagesFile && error.debugMessages) {
|
|
328
|
+
await writeTextFile(options.debugMessagesFile, error.debugMessages.map(([timestamp, message]) =>
|
|
329
|
+
`[${new Date(timestamp).toISOString()}] ${message.join(" ")}`).join("\n"));
|
|
330
|
+
}
|
|
308
331
|
}
|
|
309
332
|
}
|
|
310
333
|
|
|
@@ -338,7 +361,7 @@ async function getFilename(filename, options, index = 1) {
|
|
|
338
361
|
if (options.filenameConflictAction != "skip") {
|
|
339
362
|
return getFilename(filename, options, index + 1);
|
|
340
363
|
}
|
|
341
|
-
} catch
|
|
364
|
+
} catch {
|
|
342
365
|
return newFilename;
|
|
343
366
|
}
|
|
344
367
|
}
|
package/single-file-launcher.js
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
import { initialize } from "./single-file-cli-api.js";
|
|
25
25
|
import { closeBrowser } from "./lib/browser.js";
|
|
26
26
|
import { Deno } from "./lib/deno-polyfill.js";
|
|
27
|
-
import options from "./options.js";
|
|
27
|
+
import { options, parseArgs } from "./options.js";
|
|
28
28
|
|
|
29
29
|
const { readTextFile, readFile, exit, addSignalListener } = Deno;
|
|
30
30
|
|
|
@@ -54,7 +54,7 @@ async function run() {
|
|
|
54
54
|
delete options.settingsFile;
|
|
55
55
|
}
|
|
56
56
|
if (options.urlsFile) {
|
|
57
|
-
urls =
|
|
57
|
+
urls = await getUrlsFile(options.urlsFile);
|
|
58
58
|
} else {
|
|
59
59
|
urls = [options.url];
|
|
60
60
|
}
|
|
@@ -80,7 +80,7 @@ async function run() {
|
|
|
80
80
|
const cookiesContent = await readTextFile(options.browserCookiesFile);
|
|
81
81
|
try {
|
|
82
82
|
options.browserCookies = JSON.parse(cookiesContent);
|
|
83
|
-
} catch
|
|
83
|
+
} catch {
|
|
84
84
|
options.browserCookies = parseCookies(cookiesContent);
|
|
85
85
|
}
|
|
86
86
|
}
|
|
@@ -137,3 +137,56 @@ async function closeBrowserAndExit(code) {
|
|
|
137
137
|
await closeBrowser();
|
|
138
138
|
exit(code);
|
|
139
139
|
}
|
|
140
|
+
|
|
141
|
+
async function getUrlsFile(urlsFile) {
|
|
142
|
+
let urls = (await readTextFile(urlsFile)).split("\n");
|
|
143
|
+
urls = urls.map(value => {
|
|
144
|
+
value = value.trim();
|
|
145
|
+
let optionPosition = value.indexOf(" --");
|
|
146
|
+
if (optionPosition < 0) {
|
|
147
|
+
optionPosition = value.indexOf("\t--");
|
|
148
|
+
}
|
|
149
|
+
if (optionPosition > 0) {
|
|
150
|
+
const url = value.substring(0, optionPosition).trim();
|
|
151
|
+
const argsString = value.substring(optionPosition + 1).trim();
|
|
152
|
+
const args = [];
|
|
153
|
+
let previousCharacter, previousPreviousCharacter, lastQuoteCharacter;
|
|
154
|
+
let lastCharIndex = 0;
|
|
155
|
+
for (let currentCharIndex = 0; currentCharIndex < argsString.length; currentCharIndex++) {
|
|
156
|
+
const character = argsString[currentCharIndex];
|
|
157
|
+
if (character == lastQuoteCharacter && (previousCharacter != "\\" || previousPreviousCharacter == "\\")) {
|
|
158
|
+
args.push(argsString.substring(lastCharIndex, currentCharIndex));
|
|
159
|
+
lastQuoteCharacter = null;
|
|
160
|
+
lastCharIndex = currentCharIndex + 1;
|
|
161
|
+
} else if (!lastQuoteCharacter) {
|
|
162
|
+
if (character == "'" || character == "\"") {
|
|
163
|
+
lastQuoteCharacter = argsString[currentCharIndex];
|
|
164
|
+
lastCharIndex = currentCharIndex + 1;
|
|
165
|
+
} else {
|
|
166
|
+
const isSpaceCharacter = character == " " || character == "\t";
|
|
167
|
+
if (isSpaceCharacter || character == "=") {
|
|
168
|
+
if (isSpaceCharacter && (currentCharIndex == lastCharIndex + 1)) {
|
|
169
|
+
lastCharIndex++;
|
|
170
|
+
} else if (lastCharIndex < currentCharIndex) {
|
|
171
|
+
args.push(argsString.substring(lastCharIndex, currentCharIndex));
|
|
172
|
+
lastCharIndex = currentCharIndex + 1;
|
|
173
|
+
} else {
|
|
174
|
+
lastCharIndex = currentCharIndex + 1;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
previousPreviousCharacter = previousCharacter;
|
|
180
|
+
previousCharacter = character;
|
|
181
|
+
}
|
|
182
|
+
if (lastCharIndex < argsString.length) {
|
|
183
|
+
args.push(argsString.substring(lastCharIndex).trim());
|
|
184
|
+
}
|
|
185
|
+
const { options } = parseArgs(args, false);
|
|
186
|
+
return [url, options];
|
|
187
|
+
} else {
|
|
188
|
+
return value;
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
return urls;
|
|
192
|
+
}
|