sourcemonkey 0.0.0-stage → 0.5.0
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/LICENSE +21 -0
- package/README.md +87 -2
- package/dist/bytes.js +37 -0
- package/dist/channel-placeholder.js +5 -0
- package/dist/collection-scan.js +17 -0
- package/dist/collections.html +353 -0
- package/dist/compare.html +358 -0
- package/dist/composed-source.js +205 -0
- package/dist/connect-prompt.html +47 -0
- package/dist/debug/icons.html +211 -0
- package/dist/debug/storage.html +163 -0
- package/dist/debug-log.js +75 -0
- package/dist/diagnostic-location.js +64 -0
- package/dist/eval-hook.js +308 -0
- package/dist/extension/background.js +5263 -0
- package/dist/extension/channel.js +133 -0
- package/dist/extension/code-links.js +187 -0
- package/dist/extension/code-tabs.js +460 -0
- package/dist/extension/collection-dialog.js +619 -0
- package/dist/extension/collection-display.js +322 -0
- package/dist/extension/collections.js +2905 -0
- package/dist/extension/compare.js +867 -0
- package/dist/extension/concurrency.js +186 -0
- package/dist/extension/connect-grants.js +109 -0
- package/dist/extension/connect-prompt-page.js +70 -0
- package/dist/extension/connect-prompt.js +266 -0
- package/dist/extension/copy-button.js +63 -0
- package/dist/extension/copy-dialog.js +502 -0
- package/dist/extension/copy-preference.js +66 -0
- package/dist/extension/debug/icons.js +203 -0
- package/dist/extension/debug/storage.js +365 -0
- package/dist/extension/deep-link.js +77 -0
- package/dist/extension/delete-dialog.js +432 -0
- package/dist/extension/delete-icon.js +40 -0
- package/dist/extension/details-tooltips.js +66 -0
- package/dist/extension/directories.js +679 -0
- package/dist/extension/export-backup.js +407 -0
- package/dist/extension/export-dialog.js +142 -0
- package/dist/extension/file-access.js +74 -0
- package/dist/extension/file-changes.js +346 -0
- package/dist/extension/frame-info.js +85 -0
- package/dist/extension/github-provider.js +336 -0
- package/dist/extension/glyph.js +84 -0
- package/dist/extension/gm-bindings.js +601 -0
- package/dist/extension/gm-download.js +419 -0
- package/dist/extension/gm-menu.js +228 -0
- package/dist/extension/gm-notification.js +141 -0
- package/dist/extension/gm-originator-map.js +23 -0
- package/dist/extension/gm-tab-data.js +121 -0
- package/dist/extension/gm-tab.js +160 -0
- package/dist/extension/gm-url-change.js +111 -0
- package/dist/extension/gm-url-scheme.js +27 -0
- package/dist/extension/gm-values.js +176 -0
- package/dist/extension/gm-window-control.js +104 -0
- package/dist/extension/gm-xhr-headers.js +333 -0
- package/dist/extension/gm-xhr.js +584 -0
- package/dist/extension/injector.js +3145 -0
- package/dist/extension/install-capture.js +277 -0
- package/dist/extension/line-diff.js +233 -0
- package/dist/extension/local-copy.js +560 -0
- package/dist/extension/local-delete.js +276 -0
- package/dist/extension/location-kinds.js +108 -0
- package/dist/extension/log-format.js +158 -0
- package/dist/extension/managed-changes.js +361 -0
- package/dist/extension/managed-items.js +57 -0
- package/dist/extension/match-index.js +375 -0
- package/dist/extension/missing-scripts.js +66 -0
- package/dist/extension/more-menu.js +109 -0
- package/dist/extension/move-dialog.js +197 -0
- package/dist/extension/offscreen.js +37 -0
- package/dist/extension/options.js +504 -0
- package/dist/extension/page-heading.js +170 -0
- package/dist/extension/page-links.js +199 -0
- package/dist/extension/parse-check-client.js +70 -0
- package/dist/extension/parse-check.js +161 -0
- package/dist/extension/parse-sandbox-host.js +172 -0
- package/dist/extension/parse-sandbox.js +175 -0
- package/dist/extension/popup-commands-open.js +41 -0
- package/dist/extension/popup-view.js +387 -0
- package/dist/extension/popup.js +703 -0
- package/dist/extension/prism-userscript.js +53 -0
- package/dist/extension/refresh-error.js +136 -0
- package/dist/extension/refresh-targets.js +160 -0
- package/dist/extension/related-versions.js +1144 -0
- package/dist/extension/resize-observer-loop.js +19 -0
- package/dist/extension/row-cache.js +387 -0
- package/dist/extension/script-blobs.js +413 -0
- package/dist/extension/script-details.js +658 -0
- package/dist/extension/script-enablement.js +285 -0
- package/dist/extension/script-identity.js +310 -0
- package/dist/extension/script-keys.js +54 -0
- package/dist/extension/script-log-id.js +155 -0
- package/dist/extension/script-log.js +290 -0
- package/dist/extension/script-rejection.js +104 -0
- package/dist/extension/script-sites.js +158 -0
- package/dist/extension/script-store.js +3507 -0
- package/dist/extension/script-updater.js +991 -0
- package/dist/extension/script-url-hash.js +61 -0
- package/dist/extension/select-all.js +37 -0
- package/dist/extension/select-open-guard.js +90 -0
- package/dist/extension/site-icon.js +1261 -0
- package/dist/extension/sm-dev-link.js +302 -0
- package/dist/extension/solo-click.js +58 -0
- package/dist/extension/sticky-numbers.js +188 -0
- package/dist/extension/storage-breakdown.js +253 -0
- package/dist/extension/storage-scan.js +86 -0
- package/dist/extension/storage-usage.js +194 -0
- package/dist/extension/structural-keys.js +29 -0
- package/dist/extension/sw-teardown.js +36 -0
- package/dist/extension/tab-status.js +403 -0
- package/dist/extension/toolbar-summary.js +105 -0
- package/dist/extension/user-scripts-access.js +43 -0
- package/dist/extension/userscript.js +3672 -0
- package/dist/extension/value-store.js +204 -0
- package/dist/extension/version-probe-fetch.js +68 -0
- package/dist/extension/worker-client.js +109 -0
- package/dist/extension/worker-ops.js +171 -0
- package/dist/extension/zip-writer.js +24 -0
- package/dist/external-files.js +878 -0
- package/dist/file-loader.js +436 -0
- package/dist/file-locations.js +439 -0
- package/dist/generated-wrappers.js +5 -0
- package/dist/github-auth.js +148 -0
- package/dist/github-urls.js +46 -0
- package/dist/gm-api-catalog.js +217 -0
- package/dist/gm-connect.js +273 -0
- package/dist/gm-xhr-types.js +5 -0
- package/dist/http-read.js +142 -0
- package/dist/icons/icon-128.png +0 -0
- package/dist/icons/icon-16.png +0 -0
- package/dist/icons/icon-48.png +0 -0
- package/dist/local-file-overrides.js +558 -0
- package/dist/manifest.json +38 -0
- package/dist/markup-response.js +126 -0
- package/dist/match-targets.js +305 -0
- package/dist/menu-captions.js +22 -0
- package/dist/offscreen.html +39 -0
- package/dist/options.html +197 -0
- package/dist/parse-check-protocol.js +51 -0
- package/dist/parse-location.js +171 -0
- package/dist/parse-sandbox.html +56 -0
- package/dist/phase-timing.js +155 -0
- package/dist/popup.html +197 -0
- package/dist/public-suffixes.js +6957 -0
- package/dist/run-context.js +31 -0
- package/dist/runtime-messages.js +34 -0
- package/dist/sandboxed-page.js +35 -0
- package/dist/script-build.js +787 -0
- package/dist/script-display.js +383 -0
- package/dist/script-icon.js +43 -0
- package/dist/script-links.js +204 -0
- package/dist/script-source-url.js +214 -0
- package/dist/script-world.js +65 -0
- package/dist/sm-dev-protocol.js +84 -0
- package/dist/source-map.js +177 -0
- package/dist/stack-link.js +138 -0
- package/dist/styles.css +825 -0
- package/dist/unsafe-window-use.js +229 -0
- package/dist/url-match.js +734 -0
- package/dist/userscript-header.js +248 -0
- package/dist/userscript.html +1165 -0
- package/dist/vendor/fflate.LICENSE +21 -0
- package/dist/vendor/fflate.js +2692 -0
- package/dist/vendor/prism/prism-line-numbers.css +1 -0
- package/dist/vendor/prism/prism-line-numbers.js +1 -0
- package/dist/vendor/prism/prism.css +1 -0
- package/dist/vendor/prism/prism.js +1946 -0
- package/dist/version-compare.js +141 -0
- package/dist/version-probe.js +120 -0
- package/lib/build-info.json +1 -0
- package/lib/bytes.d.ts +3 -0
- package/lib/bytes.js +37 -0
- package/lib/channel-placeholder.d.ts +1 -0
- package/lib/channel-placeholder.js +5 -0
- package/lib/collection-scan.d.ts +8 -0
- package/lib/collection-scan.js +17 -0
- package/lib/composed-source.d.ts +28 -0
- package/lib/composed-source.js +205 -0
- package/lib/debug-log.d.ts +9 -0
- package/lib/debug-log.js +75 -0
- package/lib/diagnostic-location.d.ts +8 -0
- package/lib/diagnostic-location.js +64 -0
- package/lib/eval-hook.d.ts +34 -0
- package/lib/eval-hook.js +308 -0
- package/lib/external-files.d.ts +111 -0
- package/lib/external-files.js +878 -0
- package/lib/file-loader.d.ts +48 -0
- package/lib/file-loader.js +436 -0
- package/lib/file-locations.d.ts +44 -0
- package/lib/file-locations.js +439 -0
- package/lib/generated-wrappers.d.ts +11 -0
- package/lib/generated-wrappers.js +5 -0
- package/lib/github-auth.d.ts +12 -0
- package/lib/github-auth.js +148 -0
- package/lib/github-urls.d.ts +1 -0
- package/lib/github-urls.js +46 -0
- package/lib/gm-api-catalog.d.ts +24 -0
- package/lib/gm-api-catalog.js +217 -0
- package/lib/gm-connect.d.ts +34 -0
- package/lib/gm-connect.js +273 -0
- package/lib/gm-xhr-types.d.ts +68 -0
- package/lib/gm-xhr-types.js +5 -0
- package/lib/harness/browser-extensions.d.ts +73 -0
- package/lib/harness/browser-extensions.js +285 -0
- package/lib/harness/browser-lang.d.ts +20 -0
- package/lib/harness/browser-lang.js +86 -0
- package/lib/harness/browser-launch.d.ts +14 -0
- package/lib/harness/browser-launch.js +184 -0
- package/lib/harness/checks.d.ts +110 -0
- package/lib/harness/checks.js +321 -0
- package/lib/harness/cli.d.ts +2 -0
- package/lib/harness/cli.js +2259 -0
- package/lib/harness/command-args.d.ts +91 -0
- package/lib/harness/command-args.js +369 -0
- package/lib/harness/command-lines.d.ts +80 -0
- package/lib/harness/command-lines.js +180 -0
- package/lib/harness/command-list.d.ts +206 -0
- package/lib/harness/command-list.js +1475 -0
- package/lib/harness/console-commands.d.ts +463 -0
- package/lib/harness/console-commands.js +3216 -0
- package/lib/harness/download-watch.d.ts +21 -0
- package/lib/harness/download-watch.js +156 -0
- package/lib/harness/extension-backend.d.ts +41 -0
- package/lib/harness/extension-backend.js +100 -0
- package/lib/harness/extension-host.d.ts +112 -0
- package/lib/harness/extension-host.js +552 -0
- package/lib/harness/extension-launch.d.ts +67 -0
- package/lib/harness/extension-launch.js +285 -0
- package/lib/harness/extension-link.d.ts +315 -0
- package/lib/harness/extension-link.js +607 -0
- package/lib/harness/external-manager.d.ts +146 -0
- package/lib/harness/external-manager.js +414 -0
- package/lib/harness/fixture.d.ts +165 -0
- package/lib/harness/fixture.js +461 -0
- package/lib/harness/frame-sessions.d.ts +71 -0
- package/lib/harness/frame-sessions.js +248 -0
- package/lib/harness/frames.d.ts +68 -0
- package/lib/harness/frames.js +216 -0
- package/lib/harness/help-format.d.ts +17 -0
- package/lib/harness/help-format.js +127 -0
- package/lib/harness/host.d.ts +352 -0
- package/lib/harness/host.js +423 -0
- package/lib/harness/index.d.ts +8 -0
- package/lib/harness/index.js +10 -0
- package/lib/harness/input-file.d.ts +18 -0
- package/lib/harness/input-file.js +61 -0
- package/lib/harness/json-output.d.ts +565 -0
- package/lib/harness/json-output.js +71 -0
- package/lib/harness/manager-actions.d.ts +21 -0
- package/lib/harness/manager-actions.js +129 -0
- package/lib/harness/manager-commands.d.ts +142 -0
- package/lib/harness/manager-commands.js +1196 -0
- package/lib/harness/manager-fetch.d.ts +33 -0
- package/lib/harness/manager-fetch.js +239 -0
- package/lib/harness/managers.d.ts +24 -0
- package/lib/harness/managers.js +27 -0
- package/lib/harness/output-style.d.ts +125 -0
- package/lib/harness/output-style.js +322 -0
- package/lib/harness/page-commands.d.ts +134 -0
- package/lib/harness/page-commands.js +1141 -0
- package/lib/harness/page-output.d.ts +67 -0
- package/lib/harness/page-output.js +139 -0
- package/lib/harness/page-watch.d.ts +171 -0
- package/lib/harness/page-watch.js +607 -0
- package/lib/harness/parse-check-node.d.ts +3 -0
- package/lib/harness/parse-check-node.js +132 -0
- package/lib/harness/path-args.d.ts +22 -0
- package/lib/harness/path-args.js +148 -0
- package/lib/harness/playwright-chromium.d.ts +16 -0
- package/lib/harness/playwright-chromium.js +86 -0
- package/lib/harness/prepare.d.ts +168 -0
- package/lib/harness/prepare.js +364 -0
- package/lib/harness/prompt-editor.d.ts +17 -0
- package/lib/harness/prompt-editor.js +354 -0
- package/lib/harness/prompt-history.d.ts +13 -0
- package/lib/harness/prompt-history.js +89 -0
- package/lib/harness/rebuild-advice.d.ts +6 -0
- package/lib/harness/rebuild-advice.js +39 -0
- package/lib/harness/run-log.d.ts +47 -0
- package/lib/harness/run-log.js +140 -0
- package/lib/harness/run-logs.d.ts +132 -0
- package/lib/harness/run-logs.js +241 -0
- package/lib/harness/run.d.ts +194 -0
- package/lib/harness/run.js +1973 -0
- package/lib/harness/runs.d.ts +229 -0
- package/lib/harness/runs.js +726 -0
- package/lib/harness/save-html.d.ts +39 -0
- package/lib/harness/save-html.js +89 -0
- package/lib/harness/saved-pages.d.ts +120 -0
- package/lib/harness/saved-pages.js +275 -0
- package/lib/harness/script-mark.d.ts +17 -0
- package/lib/harness/script-mark.js +56 -0
- package/lib/harness/snapshot.d.ts +143 -0
- package/lib/harness/snapshot.js +474 -0
- package/lib/harness/stuck-tabs.d.ts +19 -0
- package/lib/harness/stuck-tabs.js +131 -0
- package/lib/harness/tab-refs.d.ts +20 -0
- package/lib/harness/tab-refs.js +80 -0
- package/lib/harness/tampermonkey.d.ts +71 -0
- package/lib/harness/tampermonkey.js +356 -0
- package/lib/harness/tm-ids.d.ts +18 -0
- package/lib/harness/tm-ids.js +88 -0
- package/lib/harness/violentmonkey.d.ts +34 -0
- package/lib/harness/violentmonkey.js +162 -0
- package/lib/harness/wait.d.ts +77 -0
- package/lib/harness/wait.js +458 -0
- package/lib/http-read.d.ts +30 -0
- package/lib/http-read.js +142 -0
- package/lib/local-file-overrides.d.ts +35 -0
- package/lib/local-file-overrides.js +558 -0
- package/lib/manifest.json +38 -0
- package/lib/markup-response.d.ts +11 -0
- package/lib/markup-response.js +126 -0
- package/lib/match-targets.d.ts +18 -0
- package/lib/match-targets.js +305 -0
- package/lib/menu-captions.d.ts +1 -0
- package/lib/menu-captions.js +22 -0
- package/lib/parse-check-protocol.d.ts +27 -0
- package/lib/parse-check-protocol.js +51 -0
- package/lib/parse-location.d.ts +12 -0
- package/lib/parse-location.js +171 -0
- package/lib/phase-timing.d.ts +12 -0
- package/lib/phase-timing.js +155 -0
- package/lib/public-suffixes.d.ts +1 -0
- package/lib/public-suffixes.js +6957 -0
- package/lib/run-context.d.ts +2 -0
- package/lib/run-context.js +31 -0
- package/lib/runtime-messages.d.ts +10 -0
- package/lib/runtime-messages.js +34 -0
- package/lib/sandboxed-page.d.ts +5 -0
- package/lib/sandboxed-page.js +35 -0
- package/lib/script-build.d.ts +111 -0
- package/lib/script-build.js +787 -0
- package/lib/script-display.d.ts +51 -0
- package/lib/script-display.js +383 -0
- package/lib/script-icon.d.ts +16 -0
- package/lib/script-icon.js +43 -0
- package/lib/script-links.d.ts +24 -0
- package/lib/script-links.js +204 -0
- package/lib/script-source-url.d.ts +8 -0
- package/lib/script-source-url.js +214 -0
- package/lib/script-world.d.ts +6 -0
- package/lib/script-world.js +65 -0
- package/lib/sm-dev-protocol.d.ts +251 -0
- package/lib/sm-dev-protocol.js +84 -0
- package/lib/source-map.d.ts +32 -0
- package/lib/source-map.js +177 -0
- package/lib/stack-link.d.ts +15 -0
- package/lib/stack-link.js +138 -0
- package/lib/unsafe-window-use.d.ts +1 -0
- package/lib/unsafe-window-use.js +229 -0
- package/lib/url-match.d.ts +40 -0
- package/lib/url-match.js +734 -0
- package/lib/userscript-header.d.ts +33 -0
- package/lib/userscript-header.js +248 -0
- package/lib/version-compare.d.ts +6 -0
- package/lib/version-compare.js +141 -0
- package/lib/version-probe.d.ts +17 -0
- package/lib/version-probe.js +120 -0
- package/package.json +55 -3
- package/skills/SourceMonkey-dev/SKILL.md +868 -0
- package/skills/SourceMonkey-dev/references/cdp.md +293 -0
- package/skills/SourceMonkey-dev/references/commands.md +297 -0
- package/skills/SourceMonkey-dev/references/other-managers.md +63 -0
- package/skills/SourceMonkey-dev/references/specs.md +46 -0
- package/skills/SourceMonkey-friction/SKILL.md +103 -0
- package/skills/SourceMonkey-friction/references/template.md +59 -0
|
@@ -0,0 +1,1475 @@
|
|
|
1
|
+
// Every sm-dev command in one list, in the sections `sm-dev help all`
|
|
2
|
+
// shows: the ones sm-dev runs itself (start, stop, ...) and the ones it
|
|
3
|
+
// sends to a run (status, click, ...), with their arguments and
|
|
4
|
+
// options. `help <command>` picks a command out of it, the prompt
|
|
5
|
+
// completes its names, and the run takes each command's flags from it.
|
|
6
|
+
//
|
|
7
|
+
// It imports only help-format.ts, json-output.ts and managers.ts, which
|
|
8
|
+
// import nothing else, so cli.ts can load it for help without the run's code.
|
|
9
|
+
import { JSON_COMMANDS } from './json-output.js';
|
|
10
|
+
import { padTo, quiet, render, textLines, visibleLength, wrap } from './help-format.js';
|
|
11
|
+
import { MANAGERS, MANAGER_NAMES } from './managers.js';
|
|
12
|
+
import { quoted } from './output-style.js';
|
|
13
|
+
// How many of a selector's matches `probe --css` and `--html` show.
|
|
14
|
+
export const PROBE_MAX = 10;
|
|
15
|
+
// How many matches `snapshot` shows by default, of a selector or --find.
|
|
16
|
+
export const PART_LIMIT = 5;
|
|
17
|
+
// How many lines `snapshot --context` lets a match's shown part grow by.
|
|
18
|
+
export const CONTEXT_LINES = 10;
|
|
19
|
+
// How the collection and script commands name a collection.
|
|
20
|
+
const COLLECTION_ARG = { term: '<collection>', about: 'A collection: `C3`, its name or a part of it, or `id=<id>`. A run\'s test collection is `D1`, by its run.' };
|
|
21
|
+
// `script uninstall --force`, which removes one of the person's own scripts.
|
|
22
|
+
const FORCE_NOTE = 'Needed outside a temp browser, where the scripts are your own.';
|
|
23
|
+
/** The longest a Node timer can wait, in seconds. */
|
|
24
|
+
export const MAX_TIMER_SECONDS = Math.floor((2 ** 31 - 1) / 1000);
|
|
25
|
+
/** The flag any command takes, listed where it waits on something (command-args.ts). */
|
|
26
|
+
export const TIMEOUT_FLAG = '--timeout-seconds';
|
|
27
|
+
/** A `--timeout-seconds` that isn't a number of seconds, on any command. */
|
|
28
|
+
export const TIMEOUT_NEEDS_SECONDS = `The --timeout-seconds flag needs a number of seconds, up to ${MAX_TIMER_SECONDS}`;
|
|
29
|
+
/** A `--timeout-seconds` value in ms, or null when it isn't a number of seconds (`TIMEOUT_NEEDS_SECONDS`). */
|
|
30
|
+
export function timeoutSecondsMs(value) {
|
|
31
|
+
const seconds = Number(value);
|
|
32
|
+
return seconds > 0 && seconds <= MAX_TIMER_SECONDS ? seconds * 1000 : null;
|
|
33
|
+
}
|
|
34
|
+
// The common syntaxes.
|
|
35
|
+
const NONE = { args: 'none' };
|
|
36
|
+
const WORDS = { args: 'words' };
|
|
37
|
+
// The rest of the line as one argument (a script, a key), so it can
|
|
38
|
+
// hold spaces.
|
|
39
|
+
const TEXT = { args: 'text' };
|
|
40
|
+
const TAB_LOADS = ['newtab', 'open', 'reload'];
|
|
41
|
+
const SETTLING = ['start', ...TAB_LOADS, 'click', 'hover', 'type', 'key'];
|
|
42
|
+
// The commands that wait on the page, so take --timeout-seconds. Those
|
|
43
|
+
// whose help lists it themselves (its default differs) aren't here.
|
|
44
|
+
const TIMED = ['start', 'click', 'hover', 'type', 'key', 'eval', 'snapshot', 'probe', 'screenshot', 'closetab', 'load', 'update'];
|
|
45
|
+
// wait log, listed under both Logs and Waiting.
|
|
46
|
+
const WAIT_LOG = { usage: 'wait log [--find=]<text>', syntax: { args: 'text' }, about: [] };
|
|
47
|
+
const WAIT_LOG_ABOUT = ['Wait for a log line with the text, or matching `/regex/`, logged since the previous command began.'];
|
|
48
|
+
// The page commands that act in an iframe with `--frame`.
|
|
49
|
+
const IN_FRAME = ['eval', 'click', 'hover', 'type', 'snapshot', 'screenshot', 'probe', 'wait selector', 'wait selector-gone', 'cdp'];
|
|
50
|
+
// `status` and `tabs` print each tab's frame tree with this.
|
|
51
|
+
const FRAMES_OPTION = { term: '--frames', about: 'Show each tab\'s iframes as a tree, with the scripts running in each (see `help frames`).' };
|
|
52
|
+
// The commands that work through SourceMonkey, which a run under
|
|
53
|
+
// another manager doesn't have.
|
|
54
|
+
const SM = ['sourcemonkey'];
|
|
55
|
+
export const HELP_SECTIONS = [
|
|
56
|
+
{
|
|
57
|
+
title: 'Command input',
|
|
58
|
+
name: 'input',
|
|
59
|
+
summary: 'Send commands from command line, stdin, or interactively',
|
|
60
|
+
text: [
|
|
61
|
+
'Commands run in order, stopping at the first that fails.',
|
|
62
|
+
'Commands come from one or more of:',
|
|
63
|
+
'1. Command-line arguments',
|
|
64
|
+
' - Multiple commands can be separated by `--`.',
|
|
65
|
+
'2. Piped input',
|
|
66
|
+
' - With `--stdin`, or with pipe input and no command-line commands.',
|
|
67
|
+
' - One command per line.',
|
|
68
|
+
'3. A prompt',
|
|
69
|
+
' - With `--interactive`, or at a terminal with no command-line commands or ending with a `start` command.',
|
|
70
|
+
],
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
title: 'Examples',
|
|
74
|
+
name: 'examples',
|
|
75
|
+
summary: 'Example syntaxes',
|
|
76
|
+
text: [
|
|
77
|
+
'Choose a browser:',
|
|
78
|
+
' # Start a test browser for this sm-dev run (the default).',
|
|
79
|
+
' sm-dev start <script> [<url>] ...',
|
|
80
|
+
'',
|
|
81
|
+
' # Launch a visible browser (if not already running).',
|
|
82
|
+
' sm-dev --open-browser [--browser <port>] [<commands...>]',
|
|
83
|
+
'',
|
|
84
|
+
' # Connect to an existing browser.',
|
|
85
|
+
' sm-dev --existing-browser [--browser <port>|<url>] <commands...>',
|
|
86
|
+
'',
|
|
87
|
+
'Open an interactive prompt (the default at a terminal):',
|
|
88
|
+
' # Optionally start an initial run first.',
|
|
89
|
+
' sm-dev [start example.user.js http://example.com]',
|
|
90
|
+
'',
|
|
91
|
+
'Start sm-dev with a tab, running a userscript:',
|
|
92
|
+
' # Start, run commands (separated by --), then exit.',
|
|
93
|
+
' sm-dev start example.user.js http://example.com -- status -- snapshot',
|
|
94
|
+
'',
|
|
95
|
+
' # Start a persistent server, then send it commands.',
|
|
96
|
+
' # Rerunning start restarts it with new arguments.',
|
|
97
|
+
' # It shuts down after an idle period, or with stop.',
|
|
98
|
+
' sm-dev start example.user.js http://example.com --detach',
|
|
99
|
+
' sm-dev status',
|
|
100
|
+
' sm-dev screenshot /tmp/image.png',
|
|
101
|
+
],
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
title: 'General commands',
|
|
105
|
+
name: 'general',
|
|
106
|
+
summary: '`help` and `exit` commands',
|
|
107
|
+
commands: [
|
|
108
|
+
{ usage: 'help', summary: 'Show help: the topics, one topic or command, or all.', about: 'Show the list of help topics.' },
|
|
109
|
+
{ usage: 'help <topic>', about: 'Show help for a topic.' },
|
|
110
|
+
{ usage: 'help <command> [subcommand]', syntax: { args: 'words', max: 2 }, about: 'Show help for a command, or one of its subcommands (`help script save`).' },
|
|
111
|
+
{ usage: 'help commands', about: 'Show a short list of all commands.' },
|
|
112
|
+
{ usage: 'help all', about: 'Show help for all topics.' },
|
|
113
|
+
{ usage: '<command> [subcommand] --help', about: 'Show help for a command, or one of its subcommands.', pointer: true },
|
|
114
|
+
{ usage: 'exit', syntax: NONE, about: 'End this sm-dev. Runs it started without `--detach` stop.' },
|
|
115
|
+
],
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
title: 'Starting a run',
|
|
119
|
+
name: 'start',
|
|
120
|
+
summary: 'Start a run: a browser, its scripts and tabs',
|
|
121
|
+
text: [
|
|
122
|
+
'A run is an sm-dev server process connected to a browser, managing a list of tabs, and using the SourceMonkey extension to run scripts on them.',
|
|
123
|
+
'The run pushes its scripts into its own test collection in the extension, which gets removed when the run ends. Injected scripts run only in the run\'s managed tabs.',
|
|
124
|
+
'The browser needs SourceMonkey installed and enabled. A temp browser (the default) has it installed automatically.',
|
|
125
|
+
'',
|
|
126
|
+
],
|
|
127
|
+
commands: [
|
|
128
|
+
{
|
|
129
|
+
usage: 'start [script]... [url]...',
|
|
130
|
+
syntax: WORDS,
|
|
131
|
+
about: ['Start a server process and connect to a browser.', 'Load the listed userscripts.', 'Open a tab for each listed URL.', 'Starts with the ID from `--run`. Stops existing runs with that ID.'],
|
|
132
|
+
args: [
|
|
133
|
+
{ term: '[script]...', about: 'Files ending in `.user.js`, directories, or globs.' },
|
|
134
|
+
{ term: '[url]...', about: ['URLs to open.', 'Default: the first script\'s default page (from `@match`), or a blank tab.'] },
|
|
135
|
+
],
|
|
136
|
+
optionGroups: [
|
|
137
|
+
{
|
|
138
|
+
title: 'Tabs at startup',
|
|
139
|
+
options: [
|
|
140
|
+
{ term: '--all', about: 'Open each script\'s default page, one tab per page.' },
|
|
141
|
+
{ term: '--notab', about: 'Open no tabs at startup. (Use `newtab` later.)' },
|
|
142
|
+
{ term: '--from-saved', about: 'Serve `.html` files as saved page snapshots, at their embedded URLs by default. See `help saved-pages`.' },
|
|
143
|
+
{ term: '--url URL', about: 'With `--from-saved` and one `.html` file: The URL to serve it at.' },
|
|
144
|
+
{ term: '--offline', about: 'With `--from-saved`: Block network requests from the saved pages.' },
|
|
145
|
+
],
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
title: 'Lifetime',
|
|
149
|
+
options: [
|
|
150
|
+
{ term: '--detach', about: 'Leave the run going when this sm-dev ends.' },
|
|
151
|
+
{ term: '--new', about: 'With `--detach`: Start a new run (don\'t stop old ones).' },
|
|
152
|
+
{ term: '--stop-after-seconds N', about: 'Stop the run after `N` seconds.' },
|
|
153
|
+
{ term: '--stop-after-idle-seconds N', about: ['Stop the run after `N` seconds with no activity.', '(Default: `1800`; `0` never stops).'] },
|
|
154
|
+
],
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
title: 'Options',
|
|
158
|
+
options: [
|
|
159
|
+
{ term: '--watch', about: 'Re-push and reload when a script or its files change.' },
|
|
160
|
+
{ term: '--values JSON', about: ['Seed `GM_*` storage before the scripts run,', 'e.g. `--values \'{"units":"metric"}\'`.'] },
|
|
161
|
+
{ term: '--values-file <path>', about: 'Seed `GM_*` storage from a file holding a JSON object.' },
|
|
162
|
+
{ term: '--lang LIST', about: 'Set the languages the run\'s tabs prefer, e.g. `--lang zh-CN` or `--lang zh-CN,en`.' },
|
|
163
|
+
],
|
|
164
|
+
},
|
|
165
|
+
],
|
|
166
|
+
},
|
|
167
|
+
],
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
title: 'Concurrent runs',
|
|
171
|
+
name: 'runs',
|
|
172
|
+
summary: 'Run IDs, work with runs',
|
|
173
|
+
text: [
|
|
174
|
+
'Each run is identified by a run ID (named `D1`, `D2`, ..., or `D<name>`).',
|
|
175
|
+
'If started with `--detach`, the run continues after sm-dev exits.',
|
|
176
|
+
'sm-dev sends commands to a running server.',
|
|
177
|
+
'If only one run is active, it\'s used by default.',
|
|
178
|
+
'Otherwise, use `--run` to choose.',
|
|
179
|
+
'',
|
|
180
|
+
],
|
|
181
|
+
commands: [
|
|
182
|
+
{ usage: 'start [script]... [url]...', about: 'Start a run, and connect to it. See `help start`.', pointer: true },
|
|
183
|
+
{ usage: 'list', syntax: NONE, about: 'List the active runs.' },
|
|
184
|
+
{ usage: 'attach [ID]', syntax: { args: 'words', max: 1 }, about: 'Connect to an existing run.' },
|
|
185
|
+
{ usage: 'stop [ID|--all]', syntax: { args: 'words', max: 1 }, about: 'Stop a run, or all runs.' },
|
|
186
|
+
],
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
title: 'Status',
|
|
190
|
+
name: 'status',
|
|
191
|
+
summary: 'Overall status for browser and runs',
|
|
192
|
+
commands: [
|
|
193
|
+
{
|
|
194
|
+
usage: 'status',
|
|
195
|
+
syntax: NONE,
|
|
196
|
+
about: [
|
|
197
|
+
'Show overall status, script list, and tab list.',
|
|
198
|
+
'With no run picked: each active run\'s status. With `--existing-browser`, also that browser and its managers.',
|
|
199
|
+
],
|
|
200
|
+
options: [FRAMES_OPTION],
|
|
201
|
+
},
|
|
202
|
+
],
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
title: 'Scripts',
|
|
206
|
+
name: 'scripts',
|
|
207
|
+
summary: 'Work with scripts',
|
|
208
|
+
commands: [
|
|
209
|
+
{ usage: 'scripts', syntax: TEXT, about: 'List the loaded scripts, and the manager\'s other scripts on the current tab.' },
|
|
210
|
+
{ usage: 'script <script>', about: 'Select the active script by ID. With `T2.3`, pick tab `T2` too.' },
|
|
211
|
+
{ usage: 'load <script>...', syntax: WORDS, about: 'Load more scripts from disk: files, directories, or globs. See `help files`.' },
|
|
212
|
+
{ usage: 'update [script]', syntax: { args: 'text' }, about: 'Re-read one (or all) scripts from disk, and reload all affected tabs.' },
|
|
213
|
+
],
|
|
214
|
+
subsections: [
|
|
215
|
+
{
|
|
216
|
+
title: 'Checking scripts',
|
|
217
|
+
name: 'checking',
|
|
218
|
+
summary: 'Match and validate scripts',
|
|
219
|
+
text: [
|
|
220
|
+
'Without a run, these check script files and URLs. In a run, they can also reference scripts and tabs by ID, or use the currently selected script and current tab\'s URL by default.',
|
|
221
|
+
'',
|
|
222
|
+
],
|
|
223
|
+
commands: [
|
|
224
|
+
{
|
|
225
|
+
usage: 'match [script]... [url]...',
|
|
226
|
+
syntax: WORDS,
|
|
227
|
+
about: 'Report whether each script would run on each page, and list the header rules that matched or excluded it.',
|
|
228
|
+
args: [
|
|
229
|
+
{ term: 'all-scripts', about: 'In a run: all loaded scripts.' },
|
|
230
|
+
{ term: 'all-tabs', about: 'In a run: all tabs.' },
|
|
231
|
+
{ term: 'all', about: 'In a run: all scripts on all tabs.' },
|
|
232
|
+
],
|
|
233
|
+
options: [{ term: '--frames', about: 'In a run: for a tab, also check each of its iframes, by the frame\'s URL. `@noframes` keeps a script out of them.' }],
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
usage: 'validate [script]...',
|
|
237
|
+
syntax: WORDS,
|
|
238
|
+
about: ['Check scripts (headers, syntax, `@require`s) without loading them.', 'With a directory, all scripts under it.'],
|
|
239
|
+
args: [{ term: 'all', about: 'In a run: all loaded scripts.' }],
|
|
240
|
+
gap: true,
|
|
241
|
+
},
|
|
242
|
+
],
|
|
243
|
+
},
|
|
244
|
+
],
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
title: 'Tabs',
|
|
248
|
+
name: 'tabs',
|
|
249
|
+
summary: 'Work with tabs',
|
|
250
|
+
commands: [
|
|
251
|
+
{ usage: 'tabs', syntax: { args: 'words', max: 1, tab: true }, about: 'List the tabs this run controls.', options: [FRAMES_OPTION] },
|
|
252
|
+
{ usage: 'tab <tab>', about: 'Select the active tab by ID.' },
|
|
253
|
+
{
|
|
254
|
+
usage: 'open [tab] [page]',
|
|
255
|
+
syntax: { args: 'words', max: 2 },
|
|
256
|
+
about: 'Open a URL in the current tab, or the tab named (`T2`).',
|
|
257
|
+
args: [
|
|
258
|
+
{ term: '<url>', about: 'A URL.' },
|
|
259
|
+
{ term: '<file>', about: 'A file, or a glob matching one.' },
|
|
260
|
+
{ term: '<script ID>', about: 'A script\'s default page: a loaded one (`2`), or an installed one (`E3`).' },
|
|
261
|
+
{ term: '(default)', about: 'The selected script\'s default page.' },
|
|
262
|
+
],
|
|
263
|
+
gap: true,
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
usage: 'newtab [page...|all]',
|
|
267
|
+
syntax: WORDS,
|
|
268
|
+
about: [
|
|
269
|
+
'Open a new tab for each page (a URL, file, glob or script ID), or a blank tab.',
|
|
270
|
+
'The first new tab is selected and focused.',
|
|
271
|
+
'With a script ID: that script\'s default URL.',
|
|
272
|
+
'With `all`: one tab for each script\'s default URL.',
|
|
273
|
+
],
|
|
274
|
+
gap: true,
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
usage: 'closetab [T<n>...|all]',
|
|
278
|
+
syntax: WORDS,
|
|
279
|
+
about: ['Close this tab, the tabs named, or all tabs.', 'A page that asks first ("Leave site?") shows its dialog, for `accept` or `dismiss`.'],
|
|
280
|
+
options: [{ term: '--force', about: 'Close without running the pages\' `beforeunload` handlers.' }],
|
|
281
|
+
gap: true,
|
|
282
|
+
},
|
|
283
|
+
{ usage: 'reload [T<n>|all]', syntax: { args: 'words', max: 1, tab: true }, about: 'Reload this tab, another tab, or all tabs.', gap: true },
|
|
284
|
+
{ usage: 'focus [T<n>]', syntax: { args: 'words', max: 1, tab: true }, about: 'Bring the tab to the front.', gap: true },
|
|
285
|
+
],
|
|
286
|
+
optionsTitle: 'Options for `open`, `newtab`, `reload`',
|
|
287
|
+
options: [
|
|
288
|
+
{ term: '--noscript', about: 'Run no scripts in the tab. (Sticky)', commands: TAB_LOADS },
|
|
289
|
+
{ term: '--script', about: 'Run the scripts in the tab again. (Undo `--noscript`.)', commands: TAB_LOADS },
|
|
290
|
+
{ term: '--timeout-seconds S', about: 'Give up on the page loading after `S` seconds (default: `30`), or settling (default: `5`).', commands: TAB_LOADS },
|
|
291
|
+
{ term: '--from-saved', about: 'Serve `.html` files as saved page snapshots, at their embedded URLs by default.', commands: ['newtab', 'open'] },
|
|
292
|
+
{ term: '--url URL', about: 'With `--from-saved` and one `.html` file: The URL to serve it at.', commands: ['newtab', 'open'] },
|
|
293
|
+
{ term: '--offline', about: 'With `--from-saved`: Block network requests from the saved pages.', commands: ['newtab', 'open'] },
|
|
294
|
+
],
|
|
295
|
+
subsections: [
|
|
296
|
+
{
|
|
297
|
+
title: 'Saved pages',
|
|
298
|
+
name: 'saved-pages',
|
|
299
|
+
summary: 'Work with saved HTML snapshots',
|
|
300
|
+
text: [
|
|
301
|
+
'With `--from-saved`, pages contents are served from saved html snapshot files. Chrome shows the page running at its original URL, so the page and its scripts work as on the live site. `--offline` blocks network requests to other live pages.',
|
|
302
|
+
'',
|
|
303
|
+
'Page URLs are taken from the header comment in the HTML file if present. Otherwise, pass the URL with `--url`.',
|
|
304
|
+
'The tab keeps serving that URL from the file, rereading it on reloads.',
|
|
305
|
+
],
|
|
306
|
+
},
|
|
307
|
+
],
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
title: 'Frames',
|
|
311
|
+
name: 'frames',
|
|
312
|
+
summary: 'Work with iframes',
|
|
313
|
+
text: [
|
|
314
|
+
'A script runs in each iframe whose own URL it matches, unless it has `@noframes`. Each iframe gets an ID, `F1`, `F2` and so on across the run, which it keeps when it reloads. The top frame goes by its tab\'s ID (`T1`).',
|
|
315
|
+
'',
|
|
316
|
+
'Working in a frame:',
|
|
317
|
+
'- `frames`: Show the frames tree, and the scripts running in each.',
|
|
318
|
+
'- `tabs` or `status` with `--frames`: Include the frames tree.',
|
|
319
|
+
'- `snapshot`: Show refs in frames too (`f2e4`), which work in any command as they are.',
|
|
320
|
+
'- `--frame F2`: Make a command act on a frame.',
|
|
321
|
+
' - A selector looks inside `F2`: `click`, `hover`, `type`, `probe`, `snapshot`, `screenshot`, `wait selector`.',
|
|
322
|
+
' - `eval` runs there: in the script\'s copy in `F2`, or with `--page` in its page.',
|
|
323
|
+
' - `cdp` sends CDP commands to `F2`\'s target.',
|
|
324
|
+
'- `match --frames`: Show what each frame\'s own URL would match.',
|
|
325
|
+
'- Log lines from a frame start with it: `[T1.F2]`.',
|
|
326
|
+
'- `menu` labels a frame\'s commands with it: `M3: Toggle (F2)`.',
|
|
327
|
+
'',
|
|
328
|
+
],
|
|
329
|
+
commands: [
|
|
330
|
+
{
|
|
331
|
+
usage: 'frames [tab|frame|all]',
|
|
332
|
+
syntax: { args: 'words', max: 1 },
|
|
333
|
+
about: 'List the current tab\'s iframes, as a tree, with the scripts running in each.',
|
|
334
|
+
args: [
|
|
335
|
+
{ term: 'T2', about: 'That tab\'s iframes.' },
|
|
336
|
+
{ term: 'F3, T2.F3', about: 'That frame and the frames in it.' },
|
|
337
|
+
{ term: 'frameId=<id>', about: 'A frame by its CDP frame id, as the `frames` tree shows it, or the start of one.' },
|
|
338
|
+
{ term: 'all', about: 'Every tab\'s iframes.' },
|
|
339
|
+
],
|
|
340
|
+
},
|
|
341
|
+
],
|
|
342
|
+
},
|
|
343
|
+
{
|
|
344
|
+
title: 'Inspecting the page',
|
|
345
|
+
name: 'inspecting',
|
|
346
|
+
summary: 'Read the page',
|
|
347
|
+
commands: [
|
|
348
|
+
{
|
|
349
|
+
usage: 'snapshot [tab] [selector]',
|
|
350
|
+
syntax: { args: 'selector', tab: true },
|
|
351
|
+
about: ['Print the page, or the selector\'s matches, as the accessibility tree, with a ref for each element.'],
|
|
352
|
+
options: [
|
|
353
|
+
{ term: '--visible', about: 'Only what is on screen.' },
|
|
354
|
+
{ term: '--out FILE', about: 'Write it to a file instead.' },
|
|
355
|
+
{ term: '--find <text>', about: 'Only elements containing this text (in a name, text, value or URL; case insensitive), within the selector\'s matches.' },
|
|
356
|
+
{ term: '--up <n>', about: 'Start from the element `n` levels above each match.' },
|
|
357
|
+
{ term: '--context', about: `Go further up from each match while that adds at most ${CONTEXT_LINES} lines: wrapper divs, small containers.` },
|
|
358
|
+
{ term: '--limit <n>', about: `How many matches to show (${quoted(PART_LIMIT)} by default, ${quoted('0')} for all).` },
|
|
359
|
+
],
|
|
360
|
+
},
|
|
361
|
+
{
|
|
362
|
+
usage: 'screenshot [tab] [selector] <file>',
|
|
363
|
+
syntax: { args: 'words', max: 2, tab: true },
|
|
364
|
+
about: 'Save a PNG of what is on screen, or of an element.',
|
|
365
|
+
options: [{ term: '--full', about: 'The whole page.' }],
|
|
366
|
+
gap: true,
|
|
367
|
+
},
|
|
368
|
+
{ usage: 'savehtml [tab] <file>', syntax: { args: 'words', max: 1, tab: true }, about: 'Save the page\'s HTML (live DOM snapshot).', gap: true },
|
|
369
|
+
{
|
|
370
|
+
usage: 'probe [tab] <selector>...',
|
|
371
|
+
syntax: { args: 'words', tab: true },
|
|
372
|
+
about: ['Look up selectors.', 'Report which are found, how many times.'],
|
|
373
|
+
options: [
|
|
374
|
+
{ term: '--css', about: `For the first ${PROBE_MAX} matches, show a unique CSS selector, if one exists.` },
|
|
375
|
+
{ term: '--html', about: `For the first ${PROBE_MAX} matches, show the start of each one's HTML.` },
|
|
376
|
+
],
|
|
377
|
+
gap: true,
|
|
378
|
+
},
|
|
379
|
+
],
|
|
380
|
+
},
|
|
381
|
+
{
|
|
382
|
+
title: 'Logs',
|
|
383
|
+
name: 'logs',
|
|
384
|
+
summary: 'Read or search logs',
|
|
385
|
+
text: [
|
|
386
|
+
'A run tracks log lines from these sources, narrowest first:',
|
|
387
|
+
'1. `sm`: SourceMonkey logs. (From a script\'s Log tab: reloads, updates, errors, etc.)',
|
|
388
|
+
'2. `event`: sm-dev script and tab events. (Starting, page loads, errors, menu commands, dialog boxes, tabs opened, requests, etc.)',
|
|
389
|
+
'3. `script`: Chrome console logs that came from the run\'s scripts.',
|
|
390
|
+
'4. `page`: Full Chrome console logs.',
|
|
391
|
+
'',
|
|
392
|
+
'Some lines are in multiple sources: a script\'s console line is also `page`, and its throw is an `event`, and also `sm`, `script` and `page`.',
|
|
393
|
+
'',
|
|
394
|
+
'By default, sm-dev prints log lines from 1-3 as they arrive.',
|
|
395
|
+
'',
|
|
396
|
+
'Each line has a log ID: when it arrived, in microseconds since the epoch. `logs` and `wait log` end by saying the log ID they read up to. Later commands can resume from there using `--since`.',
|
|
397
|
+
'',
|
|
398
|
+
],
|
|
399
|
+
commands: [
|
|
400
|
+
{
|
|
401
|
+
usage: 'logs [tab|all] [script]',
|
|
402
|
+
syntax: { args: 'words', max: 2, tab: true },
|
|
403
|
+
about: ['Print logs from one tab or all tabs (default: the current tab), from all scripts running on those tabs.', 'Optionally filter to logs from one script.'],
|
|
404
|
+
options: [
|
|
405
|
+
{ term: '--tail N', about: 'Only the last `N` lines.' },
|
|
406
|
+
{ term: '--tail PERIOD', about: 'Only the last time period, e.g. `30s` or `5m`.' },
|
|
407
|
+
],
|
|
408
|
+
},
|
|
409
|
+
{ ...WAIT_LOG, about: [...WAIT_LOG_ABOUT, 'Includes all log sources by default.'], pointer: true, gap: true },
|
|
410
|
+
],
|
|
411
|
+
optionsTitle: 'Log selection options (on `logs`, `wait log`, `start`, `attach`)',
|
|
412
|
+
options: [
|
|
413
|
+
{
|
|
414
|
+
term: '--log-source SOURCE',
|
|
415
|
+
about: [
|
|
416
|
+
'The log source to show: `sm`, `event`, `script`, `page`, or `all`. Multiple can be given, separated by commas.',
|
|
417
|
+
'On `start` and `attach`, what logs that sm-dev session shows.',
|
|
418
|
+
],
|
|
419
|
+
commands: ['start', 'attach', 'logs', 'wait log'],
|
|
420
|
+
},
|
|
421
|
+
{ term: '--find TEXT', about: 'Only log lines with the text, or matching `/regex/`.', commands: ['logs', 'wait log'] },
|
|
422
|
+
{
|
|
423
|
+
term: '--since POSITION',
|
|
424
|
+
about: 'Only log lines after a log ID or a time (e.g. `2026-10-03T14:05:12Z` or `14:05:12`).',
|
|
425
|
+
commands: ['logs', 'wait log'],
|
|
426
|
+
},
|
|
427
|
+
],
|
|
428
|
+
},
|
|
429
|
+
{
|
|
430
|
+
title: 'Driving the page',
|
|
431
|
+
name: 'driving',
|
|
432
|
+
summary: 'Act on the page: clicks, `eval` javascript, etc',
|
|
433
|
+
commands: [
|
|
434
|
+
{
|
|
435
|
+
usage: 'click [tab] <selector>',
|
|
436
|
+
syntax: { args: 'selector', tab: true },
|
|
437
|
+
about: 'Click an element.',
|
|
438
|
+
options: [
|
|
439
|
+
{ term: '--button <name>', about: '`left` (the default), `right` or `middle`.' },
|
|
440
|
+
{ term: '--modifiers <keys>', about: 'Keys held during the click, e.g. `Shift` or `Control+Alt`.' },
|
|
441
|
+
{ term: '--count <n>', about: 'Clicks in a row: `2` double-clicks, `3` triple-clicks.' },
|
|
442
|
+
{ term: '--at <x,y>', about: 'Where in the element, in pixels from its top left corner (default: its center).' },
|
|
443
|
+
],
|
|
444
|
+
},
|
|
445
|
+
{
|
|
446
|
+
usage: 'hover [tab] <selector>',
|
|
447
|
+
syntax: { args: 'selector', tab: true },
|
|
448
|
+
about: 'Move the mouse over an element.',
|
|
449
|
+
options: [
|
|
450
|
+
{ term: '--modifiers <keys>', about: 'Keys held, e.g. `Alt`.' },
|
|
451
|
+
{ term: '--at <x,y>', about: 'Where in the element, in pixels from its top left corner (default: its center).' },
|
|
452
|
+
],
|
|
453
|
+
gap: true,
|
|
454
|
+
},
|
|
455
|
+
{
|
|
456
|
+
usage: 'type [tab] <selector> <text>',
|
|
457
|
+
syntax: { args: 'text', before: 1, tab: true, block: true, bracesInText: true },
|
|
458
|
+
about: ['Type text into an element. (Text box, etc.)', '`type <selector> {{` starts multi-line text, ended by `}}`.'],
|
|
459
|
+
options: [
|
|
460
|
+
{ term: '--file <path>', about: 'Type a file\'s text, in place of `<text>`.' },
|
|
461
|
+
{ term: '--clear', about: 'Empty the element first. With no text, only empty it.' },
|
|
462
|
+
],
|
|
463
|
+
gap: true,
|
|
464
|
+
},
|
|
465
|
+
{ usage: 'key [tab] <keys>...', syntax: { args: 'words', tab: true }, about: ['Press keys in order, using common key names.', 'e.g. `key j Shift+ArrowRight Escape`'], gap: true },
|
|
466
|
+
{
|
|
467
|
+
usage: 'eval [tab] <js>',
|
|
468
|
+
syntax: { args: 'text', tab: true, block: true, unquote: 'none' },
|
|
469
|
+
about: [
|
|
470
|
+
'Evaluate JavaScript inside the selected script and print the result.',
|
|
471
|
+
'It sees the page\'s DOM, the script\'s `GM_*` APIs, its `@require` libraries and its top-level declarations.',
|
|
472
|
+
'In a script that opens with a `(function () { ... })()` wrapper, it runs inside that, seeing its locals too.',
|
|
473
|
+
'With no script selected, or one that hasn\'t run on the page, it runs in the page.',
|
|
474
|
+
'The last expression is the result, as in the DevTools console. A top-level return works too.',
|
|
475
|
+
'`eval {{` starts a multi-line block, ended by `}}`.',
|
|
476
|
+
],
|
|
477
|
+
options: [
|
|
478
|
+
{ term: '--page', about: 'Evaluate in the page instead, where the page\'s own globals are.' },
|
|
479
|
+
{
|
|
480
|
+
term: '--script[=<script>]',
|
|
481
|
+
about: 'Evaluate inside the selected script, or the one named. A script that hasn\'t run on the page is an error.',
|
|
482
|
+
},
|
|
483
|
+
{ term: '--file <path>', about: 'Evaluate a JavaScript file, in place of `<js>`.' },
|
|
484
|
+
{ term: '--out FILE', about: 'Write the result to a file instead. Printed results are cut past 10,000 characters.' },
|
|
485
|
+
],
|
|
486
|
+
gap: true,
|
|
487
|
+
},
|
|
488
|
+
],
|
|
489
|
+
subsections: [
|
|
490
|
+
{
|
|
491
|
+
title: 'Waiting',
|
|
492
|
+
name: 'waiting',
|
|
493
|
+
summary: 'Wait for some condition',
|
|
494
|
+
commands: [
|
|
495
|
+
{
|
|
496
|
+
usage: 'wait [T<n>|all]',
|
|
497
|
+
summary: 'Wait for the page to settle, an element, a log line, or a time.',
|
|
498
|
+
syntax: { args: 'words', tab: true },
|
|
499
|
+
about: [
|
|
500
|
+
'Wait until one tab, or all tabs, settles.',
|
|
501
|
+
'Meaning: The page loaded, its scripts ran, and no requests or DOM changes happened for a short interval.',
|
|
502
|
+
'Most commands doing browser actions wait by default before returning, unless they have `--nowait`.',
|
|
503
|
+
],
|
|
504
|
+
},
|
|
505
|
+
{ usage: 'wait selector <css>', syntax: { args: 'selector' }, about: 'Wait until an element matches.', gap: true },
|
|
506
|
+
{ usage: 'wait selector-gone <css>', syntax: { args: 'selector' }, about: 'Wait until no element matches.' },
|
|
507
|
+
{ ...WAIT_LOG, about: [...WAIT_LOG_ABOUT, 'See `help logs` for more options.'], gap: true },
|
|
508
|
+
{ usage: 'wait <time>', about: 'Wait that long, e.g. `500ms` or `2s`.', gap: true },
|
|
509
|
+
],
|
|
510
|
+
options: [
|
|
511
|
+
{ term: '--timeout-seconds S', about: 'Give up after `S` seconds (default: `5`).', commands: ['wait'] },
|
|
512
|
+
],
|
|
513
|
+
},
|
|
514
|
+
{
|
|
515
|
+
title: 'Sending raw CDP (Chrome DevTools Protocol) calls',
|
|
516
|
+
name: 'cdp',
|
|
517
|
+
summary: 'Send raw Chrome DevTools Protocol calls',
|
|
518
|
+
text: [
|
|
519
|
+
'Use it for what no other command does. Each `cdp` command is one CDP session.',
|
|
520
|
+
'A `{{` block or `--file` holds multiple `Method` calls, one per line, run sequentially in that session, stopping after errors.',
|
|
521
|
+
'Lines starting with `#` are comments.',
|
|
522
|
+
'`params` is a JSON object, bare or in single quotes.',
|
|
523
|
+
'',
|
|
524
|
+
],
|
|
525
|
+
commands: [
|
|
526
|
+
{
|
|
527
|
+
usage: 'cdp [tab] <Method> [params]',
|
|
528
|
+
summary: 'Send raw CDP calls to the page.',
|
|
529
|
+
syntax: { args: 'text', tab: true, block: true, unquote: 'none' },
|
|
530
|
+
about: ['Make one `Method` call, and print the result as JSON.', 'e.g. `cdp Input.insertText \'{"text": "hello"}\'`'],
|
|
531
|
+
},
|
|
532
|
+
{ usage: 'cdp [tab] {{', about: 'Make multiple `Method` calls, one per line, ended by `}}`.', gap: true },
|
|
533
|
+
{ usage: 'cdp [tab] --file <path>', about: 'Make multiple `Method` calls from a file, one per line.', gap: true },
|
|
534
|
+
],
|
|
535
|
+
options: [
|
|
536
|
+
{ term: '--timeout-seconds S', about: 'Give up on each call after `S` seconds (default: `10`).', commands: ['cdp'] },
|
|
537
|
+
],
|
|
538
|
+
},
|
|
539
|
+
{
|
|
540
|
+
title: 'Menus',
|
|
541
|
+
name: 'menus',
|
|
542
|
+
summary: 'Use userscript menus',
|
|
543
|
+
commands: [
|
|
544
|
+
{
|
|
545
|
+
managers: SM,
|
|
546
|
+
usage: 'menu [what]',
|
|
547
|
+
syntax: { args: 'text', unquote: 'none' },
|
|
548
|
+
about: 'List the tab\'s menu commands, or fire one.',
|
|
549
|
+
args: [
|
|
550
|
+
{ term: 'T2', about: 'List a tab\'s.' },
|
|
551
|
+
{ term: 'all', about: 'List all tabs\'.' },
|
|
552
|
+
{ term: 'M2, T2.M2', about: 'Fire one, by number.' },
|
|
553
|
+
{ term: '<label>', about: 'Fire one, by its label.' },
|
|
554
|
+
],
|
|
555
|
+
},
|
|
556
|
+
],
|
|
557
|
+
options: [
|
|
558
|
+
{ term: '--timeout-seconds S', about: 'Wait up to `S` seconds for the command to be registered.', commands: ['menu'] },
|
|
559
|
+
],
|
|
560
|
+
},
|
|
561
|
+
{
|
|
562
|
+
title: 'Stored values',
|
|
563
|
+
name: 'stored-values',
|
|
564
|
+
summary: 'View and update userscript GM storage',
|
|
565
|
+
commands: [
|
|
566
|
+
{
|
|
567
|
+
managers: SM,
|
|
568
|
+
usage: 'values [what]',
|
|
569
|
+
syntax: TEXT,
|
|
570
|
+
about: 'Print values scripts have stored in GM Storage.',
|
|
571
|
+
args: [
|
|
572
|
+
{ term: '<script>', about: 'One script.' },
|
|
573
|
+
{ term: 'all', about: 'All scripts. (Default)' },
|
|
574
|
+
{ term: 'T2', about: 'Scripts running on a tab.' },
|
|
575
|
+
],
|
|
576
|
+
},
|
|
577
|
+
{ managers: SM, usage: 'get <key>', syntax: TEXT, about: 'Print one value for the selected script.', gap: true },
|
|
578
|
+
{
|
|
579
|
+
managers: SM,
|
|
580
|
+
usage: 'set <key> <json>',
|
|
581
|
+
syntax: { args: 'text', before: 1, block: true, unquote: 'single' },
|
|
582
|
+
about: ['Store a value for the selected script.', '`set <key> {{` starts a multi-line block of JSON, ended by `}}`.'],
|
|
583
|
+
options: [
|
|
584
|
+
{ term: '--file <path>', about: 'Read the JSON from a file, in place of `<json>`.' },
|
|
585
|
+
{ term: '--force', about: 'Needed to change the real stored data of a SourceMonkey-managed script (`E1`, ...) outside a temp browser.' },
|
|
586
|
+
],
|
|
587
|
+
},
|
|
588
|
+
],
|
|
589
|
+
},
|
|
590
|
+
{
|
|
591
|
+
title: 'Dialog boxes',
|
|
592
|
+
name: 'dialogs',
|
|
593
|
+
summary: 'Answer dialog boxes',
|
|
594
|
+
commands: [
|
|
595
|
+
{ usage: 'accept [tab] [text]', syntax: { args: 'text', tab: true }, about: 'Answer an open dialog with OK, and a prompt with the text.' },
|
|
596
|
+
{ usage: 'dismiss [tab]', syntax: { args: 'none', tab: true }, about: 'Answer an open dialog with Cancel.' },
|
|
597
|
+
{
|
|
598
|
+
managers: SM,
|
|
599
|
+
usage: 'connect once|cancel|allow|deny|allow-all [host]',
|
|
600
|
+
syntax: { args: 'words', max: 2 },
|
|
601
|
+
about: [
|
|
602
|
+
'Answer SourceMonkey\'s permission prompt for the selected script to connect to a host its `@connect` lines don\'t cover.',
|
|
603
|
+
'`once` or `cancel` are one-time answers. `allow`, `deny` or `allow-all` are persistent for the script.',
|
|
604
|
+
'Answers the script\'s oldest waiting permission prompt, or the one for `host`.',
|
|
605
|
+
],
|
|
606
|
+
},
|
|
607
|
+
],
|
|
608
|
+
},
|
|
609
|
+
],
|
|
610
|
+
},
|
|
611
|
+
{
|
|
612
|
+
title: 'Userscript managers',
|
|
613
|
+
name: 'managers',
|
|
614
|
+
summary: 'Install and control userscript managers',
|
|
615
|
+
text: [
|
|
616
|
+
'sm-dev works with one userscript manager, picked with `--manager`: SourceMonkey (the default), Tampermonkey or Violentmonkey. A run works with the one it started with.',
|
|
617
|
+
'Under Tampermonkey or Violentmonkey, a run installs its scripts into it, marked as the run\'s, and removes them when it ends. The manager runs all its scripts in every tab, so `scripts` and `status` show the ones each tab is expected to run, by their match rules.',
|
|
618
|
+
'The commands that work through SourceMonkey alone (`values`, `menu`, `connect`, ...) are refused under the others.',
|
|
619
|
+
'A temp browser has the manager installed. Another browser gets it with `manager install`.',
|
|
620
|
+
'A manager enabled beside the chosen one runs its scripts too, so sm-dev warns of one when it connects, and in `status`.',
|
|
621
|
+
'These act on the run\'s browser, with a run picked (by `--run`, `start` or `attach`). Without one, they need `--existing-browser` or `--open-browser`.',
|
|
622
|
+
'',
|
|
623
|
+
],
|
|
624
|
+
commands: [
|
|
625
|
+
{
|
|
626
|
+
usage: 'manager fetch',
|
|
627
|
+
summary: 'Fetch, install, enable or disable the userscript manager.',
|
|
628
|
+
syntax: NONE,
|
|
629
|
+
about: 'Download Tampermonkey or Violentmonkey from the Chrome Web Store into `~/.cache/sm-dev/managers`, or update it there to the newest version.',
|
|
630
|
+
},
|
|
631
|
+
{
|
|
632
|
+
usage: 'manager install',
|
|
633
|
+
syntax: NONE,
|
|
634
|
+
about: [
|
|
635
|
+
'Load the manager into the browser as an unpacked extension, with Allow User Scripts on: this sm-dev\'s SourceMonkey, or the fetched copy of another, fetched first if need be.',
|
|
636
|
+
'A copy installed from there before is updated to the version fetched since.',
|
|
637
|
+
],
|
|
638
|
+
},
|
|
639
|
+
{ usage: 'manager enable', syntax: NONE, about: 'Turn the manager on in the browser.' },
|
|
640
|
+
{ usage: 'manager disable', syntax: NONE, about: 'Turn the manager off in the browser. Not SourceMonkey while a run uses it.' },
|
|
641
|
+
],
|
|
642
|
+
},
|
|
643
|
+
{
|
|
644
|
+
title: 'Managing installed collections and scripts',
|
|
645
|
+
name: 'installed',
|
|
646
|
+
summary: 'Manage installed collections and scripts',
|
|
647
|
+
text: [
|
|
648
|
+
'What SourceMonkey has installed. Collections are named `C1`, `C2`, ... and scripts `E1`, `E2`, ..., by the numbers SourceMonkey keeps for them.',
|
|
649
|
+
'Under Tampermonkey or Violentmonkey, its scripts are `E1`, `E2`, ..., in one collection, `C0`, whose `enable` and `disable` are its switch for all scripts. Violentmonkey\'s are its own numbers; Tampermonkey\'s are ones sm-dev keeps for it.',
|
|
650
|
+
'In a run, these use the run\'s browser. Without one, they need `--existing-browser` or `--open-browser`.',
|
|
651
|
+
],
|
|
652
|
+
subsections: [
|
|
653
|
+
{
|
|
654
|
+
title: 'Collections',
|
|
655
|
+
name: 'collections',
|
|
656
|
+
summary: 'Manage SourceMonkey collections',
|
|
657
|
+
commands: [
|
|
658
|
+
{
|
|
659
|
+
usage: 'collection list',
|
|
660
|
+
summary: 'List, show, enable, disable, refresh or rename collections.',
|
|
661
|
+
syntax: NONE,
|
|
662
|
+
about: [
|
|
663
|
+
'List the collections, with their kind, location, script count, and whether they are enabled.',
|
|
664
|
+
'A local collection with files changed on disk since it loaded shows `Refresh pending`, as the collections page does.',
|
|
665
|
+
'`collection`, `collections` and `collections list` do the same.',
|
|
666
|
+
],
|
|
667
|
+
},
|
|
668
|
+
{ usage: 'collection <collection> show', syntax: NONE, about: 'Show one collection, and list its scripts.', args: [COLLECTION_ARG] },
|
|
669
|
+
{ usage: 'collection <collection> enable', syntax: NONE, about: 'Turn a collection back on: its scripts run again, except ones turned off on their own.' },
|
|
670
|
+
{ usage: 'collection <collection> disable', syntax: NONE, about: 'Turn all of a collection\'s scripts off.' },
|
|
671
|
+
{
|
|
672
|
+
usage: 'collection <collection> refresh',
|
|
673
|
+
syntax: NONE,
|
|
674
|
+
about: 'Re-read a collection from its location: rescan a directory, re-read a manifest, or re-read each script of a container collection.',
|
|
675
|
+
},
|
|
676
|
+
{ usage: 'collection <collection> rename <name>', syntax: { args: 'words' }, about: 'Rename a collection.' },
|
|
677
|
+
],
|
|
678
|
+
},
|
|
679
|
+
{
|
|
680
|
+
title: 'Scripts',
|
|
681
|
+
name: 'installed-scripts',
|
|
682
|
+
summary: 'Manage installed scripts',
|
|
683
|
+
text: [
|
|
684
|
+
'The script comes before the verb: `script E4 enable`. Left out, it is the selected script (not for `uninstall`).',
|
|
685
|
+
'Turning a script on, by `enable`, `install` or `move`, turns off its other copies that were running, as SourceMonkey\'s script page does. Each one turned off is listed.',
|
|
686
|
+
'',
|
|
687
|
+
],
|
|
688
|
+
commands: [
|
|
689
|
+
{
|
|
690
|
+
usage: 'script list [collection]',
|
|
691
|
+
summary: 'List, show, print, enable, disable, refresh, install, uninstall or move installed scripts.',
|
|
692
|
+
syntax: { args: 'words', max: 1 },
|
|
693
|
+
about: ['List the installed scripts, under their collections, or one collection\'s. `scripts list` does the same.', 'A run\'s own scripts show as `1`, `2`, ..., and another run\'s by name only. Under Tampermonkey or Violentmonkey, a run\'s show by their `E` IDs, marked as the run\'s. The selected script is marked `*`.', 'A local file changed or deleted since it loaded shows as modified or deleted on disk.'],
|
|
694
|
+
args: [COLLECTION_ARG],
|
|
695
|
+
},
|
|
696
|
+
{
|
|
697
|
+
usage: 'script [script] show',
|
|
698
|
+
syntax: NONE,
|
|
699
|
+
about: 'Show one script in full: its header, status, files, and in a run, what it does on each tab.',
|
|
700
|
+
args: [{
|
|
701
|
+
term: '[script]',
|
|
702
|
+
about: ['A script: `E12`, its name or a part of it, or `id=<id>`. In a run, one of its own scripts by its number (`2`).', 'Without one, the selected script.'],
|
|
703
|
+
}],
|
|
704
|
+
},
|
|
705
|
+
{
|
|
706
|
+
usage: 'script [script] print',
|
|
707
|
+
syntax: NONE,
|
|
708
|
+
about: 'Print a script\'s installed text, or one of its `@require`, `@resource` or icon files.',
|
|
709
|
+
options: [
|
|
710
|
+
{
|
|
711
|
+
term: '--file <name>',
|
|
712
|
+
about: 'The file to print: part of one file\'s URL, a `@resource`\'s name, `script` or `icon`.',
|
|
713
|
+
},
|
|
714
|
+
{ term: '--out <file>', about: 'Write it to a file instead. A file that isn\'t text must use `--out`.' },
|
|
715
|
+
],
|
|
716
|
+
},
|
|
717
|
+
{
|
|
718
|
+
usage: 'script [script] save <dir>',
|
|
719
|
+
syntax: { args: 'words', max: 1 },
|
|
720
|
+
about: [
|
|
721
|
+
'Save a script and its `@require`, `@resource` and `@icon` files into a directory, which is created if necessary.',
|
|
722
|
+
'By default, each file is saved in directories matching its URL (`cdn.example.com/lib/x.js`).',
|
|
723
|
+
],
|
|
724
|
+
options: [
|
|
725
|
+
{
|
|
726
|
+
term: '--include <parts>',
|
|
727
|
+
about: 'A comma-separated list of items to save: `script`, `requires`, `resources`, `icon`, `site-icons`, or `all`, for all but `site-icons`. (Default: `all`)',
|
|
728
|
+
},
|
|
729
|
+
{ term: '--flat', about: 'Save all files in the target directory.' },
|
|
730
|
+
{ term: '--force', about: 'Overwrite files that already exist.' },
|
|
731
|
+
],
|
|
732
|
+
},
|
|
733
|
+
{ usage: 'script [script] enable', syntax: NONE, about: 'Turn a script on. In a run, one of its own scripts too, in the run\'s tabs.' },
|
|
734
|
+
{ usage: 'script [script] disable', syntax: NONE, about: 'Turn a script off. In a run, one of its own scripts too, in the run\'s tabs.' },
|
|
735
|
+
{
|
|
736
|
+
usage: 'script [script] refresh',
|
|
737
|
+
syntax: NONE,
|
|
738
|
+
about: ['Re-read a script from its location, as its `Refresh` button does.', 'One of the run\'s own scripts is re-read from disk, as `update` does.'],
|
|
739
|
+
},
|
|
740
|
+
{
|
|
741
|
+
usage: 'script install <file|url>',
|
|
742
|
+
syntax: { args: 'words', max: 1 },
|
|
743
|
+
about: [
|
|
744
|
+
'Install a `.user.js` file or URL in SourceMonkey, for every tab, to stay. (`load` adds one to the run instead, for its tabs only.)',
|
|
745
|
+
'Under Tampermonkey or Violentmonkey, it replaces the script with the same name and namespace.',
|
|
746
|
+
],
|
|
747
|
+
options: [
|
|
748
|
+
{
|
|
749
|
+
term: '--collection <collection>',
|
|
750
|
+
about: 'The container collection to install it in. (Default: the one SourceMonkey keeps for its kind: Scripts from local disk, from web, or from GitHub)',
|
|
751
|
+
},
|
|
752
|
+
{ term: '--force', about: 'Under Tampermonkey or Violentmonkey, outside a temp browser: replace a script with the same name and namespace.' },
|
|
753
|
+
],
|
|
754
|
+
},
|
|
755
|
+
{
|
|
756
|
+
usage: 'script <script> uninstall',
|
|
757
|
+
syntax: NONE,
|
|
758
|
+
about: 'Remove a script from its container collection. A directory or manifest collection\'s scripts come from its files, so they can\'t be.',
|
|
759
|
+
options: [{ term: '--force', about: FORCE_NOTE }],
|
|
760
|
+
},
|
|
761
|
+
{
|
|
762
|
+
usage: 'script [script] move <collection>',
|
|
763
|
+
syntax: { args: 'words', max: 1 },
|
|
764
|
+
about: 'Move a script from one container collection to another.',
|
|
765
|
+
},
|
|
766
|
+
],
|
|
767
|
+
},
|
|
768
|
+
],
|
|
769
|
+
},
|
|
770
|
+
{
|
|
771
|
+
title: 'Flags',
|
|
772
|
+
name: 'flags',
|
|
773
|
+
summary: 'Global and command flags',
|
|
774
|
+
subsections: [
|
|
775
|
+
{
|
|
776
|
+
title: 'Global options',
|
|
777
|
+
name: 'global-flags',
|
|
778
|
+
summary: 'sm-dev flags (before the command)',
|
|
779
|
+
text: ['These go first, before any command, and apply for later commands.'],
|
|
780
|
+
subsections: [
|
|
781
|
+
{
|
|
782
|
+
title: 'Where to get input commands (after processing command-line arguments)',
|
|
783
|
+
options: [
|
|
784
|
+
{ term: '--stdin', about: ['Read commands from stdin.', 'This is the default when command-line has no commands and stdin is piped input.'] },
|
|
785
|
+
{ term: '--interactive', about: ['Read commands interactively from a prompt.', 'This is the default when command-line has no commands and stdin and stdout are a terminal.'] },
|
|
786
|
+
],
|
|
787
|
+
},
|
|
788
|
+
{
|
|
789
|
+
title: 'Browser mode',
|
|
790
|
+
text: ['Give at most one. It picks the browser `start` uses, and for commands without a run, the browser they act on.'],
|
|
791
|
+
options: [
|
|
792
|
+
{ term: '--temp-browser', about: ['(Default) Launch a browser for each run, with an empty profile and the `--manager` manager installed.', 'Close it when the run ends.'] },
|
|
793
|
+
{
|
|
794
|
+
term: '--open-browser',
|
|
795
|
+
about: [
|
|
796
|
+
'Use the browser at `--browser`, starting it first if none is running there.',
|
|
797
|
+
'It stays open after sm-dev ends. Uses Google Chrome if installed, else Chromium.',
|
|
798
|
+
'Used alone (`sm-dev --open-browser`), just start the browser.',
|
|
799
|
+
],
|
|
800
|
+
},
|
|
801
|
+
{ term: '--existing-browser', about: 'Connect to a browser already running at `--browser`. Assumes the manager is already installed (see `manager install`).' },
|
|
802
|
+
],
|
|
803
|
+
},
|
|
804
|
+
{
|
|
805
|
+
title: 'Browser options',
|
|
806
|
+
options: [
|
|
807
|
+
{
|
|
808
|
+
term: '--browser URL|PORT',
|
|
809
|
+
about: [
|
|
810
|
+
'With `--open-browser` or `--existing-browser`: the browser\'s address, as a URL, `host:port`, or port. `--open-browser` needs a local port.',
|
|
811
|
+
'(Default: `$SM_DEV_BROWSER` or `9233`)',
|
|
812
|
+
'With a temp browser: a local port for it. Fails if the port is in use.',
|
|
813
|
+
'(Default: a free port)',
|
|
814
|
+
],
|
|
815
|
+
commands: ['start'],
|
|
816
|
+
},
|
|
817
|
+
{
|
|
818
|
+
term: '--manager NAME',
|
|
819
|
+
about: [
|
|
820
|
+
'The userscript manager to work with: `sm`, `tm` or `vm`, or `sourcemonkey`, `tampermonkey` or `violentmonkey`. See `help managers`.',
|
|
821
|
+
'A temp browser has it installed automatically.',
|
|
822
|
+
'(Default: `sm`, or a run\'s own)',
|
|
823
|
+
],
|
|
824
|
+
},
|
|
825
|
+
{ term: '--headed, --headless', about: ['When launching a browser: show its window or not.', '(Default: headed for `--open-browser`, and for a temp browser in `--interactive` mode; headless otherwise)'] },
|
|
826
|
+
{ term: '--mute, --nomute', about: ['When launching a browser: turn its sound off or on.', '(Default: muted for a temp browser, sound on for `--open-browser`)'] },
|
|
827
|
+
{ term: '--profile DIR', about: ['With `--open-browser`: the browser profile, kept between launches.', '(Default: `~/.config/sm-dev/browser-profile-<port>`)'] },
|
|
828
|
+
],
|
|
829
|
+
},
|
|
830
|
+
{
|
|
831
|
+
title: 'Server selection',
|
|
832
|
+
options: [
|
|
833
|
+
{
|
|
834
|
+
term: '--run ID',
|
|
835
|
+
about: [
|
|
836
|
+
'A label to identify which sm-dev instance to use, or what to label a newly started run.',
|
|
837
|
+
'Only needed when multiple are active.',
|
|
838
|
+
'Default new names are `D1`, `D2`, etc.',
|
|
839
|
+
'If only one run is active, it\'s chosen by default.',
|
|
840
|
+
],
|
|
841
|
+
},
|
|
842
|
+
],
|
|
843
|
+
},
|
|
844
|
+
{
|
|
845
|
+
title: 'Output',
|
|
846
|
+
options: [
|
|
847
|
+
{
|
|
848
|
+
term: '--json[=single|multi]',
|
|
849
|
+
about: [
|
|
850
|
+
'Print results as JSON. (Supported for most commands.)',
|
|
851
|
+
'Each result is on one line (`single`), or multi-line (`multi`).',
|
|
852
|
+
'(Default: `multi` at a terminal, `single` when piped)',
|
|
853
|
+
'Error and status messages go to stderr.',
|
|
854
|
+
],
|
|
855
|
+
},
|
|
856
|
+
],
|
|
857
|
+
},
|
|
858
|
+
{
|
|
859
|
+
title: 'Search paths',
|
|
860
|
+
options: [
|
|
861
|
+
{
|
|
862
|
+
term: '--root DIR',
|
|
863
|
+
about: [
|
|
864
|
+
'The repo root, searched for local copies of `@require` URLs.',
|
|
865
|
+
'(Default: the current directory)',
|
|
866
|
+
'A script outside this repo uses its own collection\'s root, the nearest directory above it with a `script_manifest.json`.',
|
|
867
|
+
],
|
|
868
|
+
},
|
|
869
|
+
{ term: '--local DIR', about: 'Additional directory to search. Can repeat.' },
|
|
870
|
+
],
|
|
871
|
+
},
|
|
872
|
+
],
|
|
873
|
+
},
|
|
874
|
+
{
|
|
875
|
+
title: 'Common options',
|
|
876
|
+
name: 'common-flags',
|
|
877
|
+
summary: 'Common command flags (after the command)',
|
|
878
|
+
text: ['These apply for multiple commands.'],
|
|
879
|
+
options: [
|
|
880
|
+
{
|
|
881
|
+
term: '--nowait',
|
|
882
|
+
about: ['Don\'t wait for the page to settle after the action.', 'By default, most page actions wait for the page to settle before continuing.'],
|
|
883
|
+
commands: SETTLING,
|
|
884
|
+
},
|
|
885
|
+
{
|
|
886
|
+
term: '--timeout-seconds S',
|
|
887
|
+
about: [
|
|
888
|
+
'For all commands that wait: the longest each wait may take, in place of its default.',
|
|
889
|
+
'e.g. an element being ready to click (default: `5`), a page loading (`30`) or settling (`5`), a screenshot (`5`).',
|
|
890
|
+
],
|
|
891
|
+
commands: TIMED,
|
|
892
|
+
},
|
|
893
|
+
{
|
|
894
|
+
term: '--frame <frame>',
|
|
895
|
+
about: ['Act in an iframe (`F2`) of the tab: a selector looks inside it, and `eval` runs there.', 'See `help frames`.'],
|
|
896
|
+
commands: IN_FRAME,
|
|
897
|
+
},
|
|
898
|
+
{
|
|
899
|
+
term: '--json[=single|multi]',
|
|
900
|
+
about: ['Print the result as JSON: on one line (`single`), or multi-line (`multi`).', '(Default: the global `--json`\'s, else `multi` at a terminal and `single` when piped)'],
|
|
901
|
+
commands: JSON_COMMANDS,
|
|
902
|
+
},
|
|
903
|
+
{
|
|
904
|
+
term: '--nojson',
|
|
905
|
+
about: 'Print the result as text, despite the global `--json`.',
|
|
906
|
+
commands: JSON_COMMANDS,
|
|
907
|
+
},
|
|
908
|
+
// All commands take it, but it's read apart from their flags, so no
|
|
909
|
+
// command lists it as one of its own.
|
|
910
|
+
{ term: '--help, -h', about: 'Show the command\'s help instead of running it.' },
|
|
911
|
+
],
|
|
912
|
+
},
|
|
913
|
+
],
|
|
914
|
+
},
|
|
915
|
+
{
|
|
916
|
+
title: 'Specifying objects',
|
|
917
|
+
name: 'objects',
|
|
918
|
+
summary: 'IDs for scripts, tabs, frames, elements and more',
|
|
919
|
+
text: ['Each object type has a unique keyspace. Some commands determine object type based on these rules.'],
|
|
920
|
+
subsections: [
|
|
921
|
+
{
|
|
922
|
+
title: 'Scripts',
|
|
923
|
+
items: [
|
|
924
|
+
{ term: '2', about: 'Script ID in the `scripts` list.' },
|
|
925
|
+
{ term: 'T2.3', about: 'Script `3`, on tab `T2`.' },
|
|
926
|
+
{ term: 'E1', about: 'An installed script, by its number in `script list`.' },
|
|
927
|
+
{ term: '<name>', about: ['Its name, file name or path, or a unique substring.', '(Only in commands that expect script names.)'] },
|
|
928
|
+
{ term: '<file>', about: 'A script file. (Ending with `.user.js`)' },
|
|
929
|
+
{ term: '<url>', about: 'A script file loaded from a URL. (Ending with `.user.js`)' },
|
|
930
|
+
],
|
|
931
|
+
},
|
|
932
|
+
{
|
|
933
|
+
title: 'Collections (in SourceMonkey)',
|
|
934
|
+
items: [
|
|
935
|
+
{ term: 'C3', about: 'Its number in `collection list`.' },
|
|
936
|
+
{ term: 'D1', about: 'A run\'s test collection, by its run.' },
|
|
937
|
+
{ term: '<name>', about: 'Its name, or a unique substring.' },
|
|
938
|
+
{ term: 'id=<id>', about: 'SourceMonkey\'s own id for it.' },
|
|
939
|
+
],
|
|
940
|
+
},
|
|
941
|
+
{
|
|
942
|
+
title: 'Pages',
|
|
943
|
+
items: [
|
|
944
|
+
{ term: '<url>', about: 'A URL. Protocol (e.g. `http://`) is optional.' },
|
|
945
|
+
{ term: '<file>', about: 'A file on disk, interpreted as a `file://` URL.' },
|
|
946
|
+
],
|
|
947
|
+
},
|
|
948
|
+
{
|
|
949
|
+
title: 'Tabs',
|
|
950
|
+
items: [
|
|
951
|
+
{ term: 'T2', about: 'Tab ID in the `tabs` list.' },
|
|
952
|
+
{ term: 'targetId=<id>', about: ['Its CDP (Chrome DevTools Protocol) target id.', 'Or its first 6 or more characters. `targetId=` is optional.'] },
|
|
953
|
+
],
|
|
954
|
+
},
|
|
955
|
+
{
|
|
956
|
+
title: 'Frames',
|
|
957
|
+
items: [
|
|
958
|
+
{ term: 'F2', about: 'Iframe ID in the `frames` list. The top frame is its tab\'s ID (`T2`).' },
|
|
959
|
+
{ term: 'T2.F2', about: 'Frame `F2`, on tab `T2`.' },
|
|
960
|
+
{ term: 'frameId=<id>', about: ['Its CDP frame id, as the `frames` tree shows it, or its first 6 or more characters.', 'Frames with their own process get their own `targetId`, which is the same as their `frameId`.'] },
|
|
961
|
+
],
|
|
962
|
+
},
|
|
963
|
+
{
|
|
964
|
+
title: 'HTML Elements',
|
|
965
|
+
items: [
|
|
966
|
+
{ term: '<selector>', about: 'A CSS or Playwright selector.' },
|
|
967
|
+
{ term: 'e4, f2e6', about: 'Its ref in the latest `snapshot`. Also `ref=e4` or `[ref=e4]`.' },
|
|
968
|
+
],
|
|
969
|
+
},
|
|
970
|
+
{
|
|
971
|
+
title: 'Menu items (for userscript menus)',
|
|
972
|
+
items: [
|
|
973
|
+
{ term: 'M2', about: 'Its number in the tab\'s `menu` list.' },
|
|
974
|
+
{ term: 'T2.M2', about: 'Menu command `M2`, on tab `T2`.' },
|
|
975
|
+
{ term: '<label>', about: 'Its label.' },
|
|
976
|
+
],
|
|
977
|
+
},
|
|
978
|
+
{
|
|
979
|
+
title: 'Runs (instances of sm-dev)',
|
|
980
|
+
items: [
|
|
981
|
+
{ term: 'D2', about: 'Its number, as reported by `start`, `status`, `list`.' },
|
|
982
|
+
{ term: 'Dname', about: 'A user-chosen ID passed in with `--run`.' },
|
|
983
|
+
],
|
|
984
|
+
},
|
|
985
|
+
],
|
|
986
|
+
},
|
|
987
|
+
{
|
|
988
|
+
title: 'Files and file patterns',
|
|
989
|
+
name: 'files',
|
|
990
|
+
summary: 'File names, directories and glob patterns',
|
|
991
|
+
text: ['Commands that take filenames accept:'],
|
|
992
|
+
items: [
|
|
993
|
+
{ term: '<filename>', about: 'A file name, optionally with a path.' },
|
|
994
|
+
{ term: '<directory>', about: 'Matches all `.user.js` files under it.' },
|
|
995
|
+
{
|
|
996
|
+
term: '<glob>',
|
|
997
|
+
about: [
|
|
998
|
+
'Matches files against a pattern, e.g. `src/*.user.js`.',
|
|
999
|
+
'`*` matches any characters in one path part.',
|
|
1000
|
+
'`**` matches zero or more directories. It must be a whole directory part, and not the last.',
|
|
1001
|
+
'Fail if no files match.',
|
|
1002
|
+
],
|
|
1003
|
+
},
|
|
1004
|
+
],
|
|
1005
|
+
},
|
|
1006
|
+
];
|
|
1007
|
+
const INTRO = [
|
|
1008
|
+
'**sm-dev**: develop and test userscripts against a real browser,',
|
|
1009
|
+
'with the SourceMonkey extension running them.',
|
|
1010
|
+
'',
|
|
1011
|
+
`Usage: ${quiet('sm-dev [global options] <command> [options] [-- <command> ...]')}`,
|
|
1012
|
+
];
|
|
1013
|
+
// Where every description starts, and where lines wrap.
|
|
1014
|
+
const ABOUT_COLUMN = 22;
|
|
1015
|
+
const WIDTH = 79;
|
|
1016
|
+
// Text as a code span: typed or shown as is.
|
|
1017
|
+
const code = (text) => quoted(text);
|
|
1018
|
+
// A description's lines, each of its own lines wrapped to `width`.
|
|
1019
|
+
function aboutLines(about, width) {
|
|
1020
|
+
return (typeof about === 'string' ? [about] : about).flatMap((line) => wrap(line, width));
|
|
1021
|
+
}
|
|
1022
|
+
// A term and its description, the description at `column`; a term too
|
|
1023
|
+
// long for that gets a line of its own. Spaces where a list of names
|
|
1024
|
+
// (`help commands`) has its bullet.
|
|
1025
|
+
function itemLines(term, about, indent, column = ABOUT_COLUMN, bullet = false) {
|
|
1026
|
+
const pad = ' '.repeat(column);
|
|
1027
|
+
const about_ = aboutLines(about, WIDTH - column).map((line) => pad + line);
|
|
1028
|
+
const lead = `${' '.repeat(indent)}${bullet ? '- ' : ' '}${quiet(term)}`;
|
|
1029
|
+
// At least two spaces before the text, or it reads as part of the term.
|
|
1030
|
+
if (visibleLength(lead) + 2 > column)
|
|
1031
|
+
return [lead, ...about_];
|
|
1032
|
+
return [padTo(lead, column) + (about_[0]?.trimStart() ?? ''), ...about_.slice(1)];
|
|
1033
|
+
}
|
|
1034
|
+
// A heading at `depth` (0 for a top section): `## Logs [`help logs`]`.
|
|
1035
|
+
function heading(title, depth) {
|
|
1036
|
+
return `${'#'.repeat(depth + 2)} ${title}`;
|
|
1037
|
+
}
|
|
1038
|
+
// A section's title, and its help topic: Logs [`help logs`].
|
|
1039
|
+
const sectionHeading = (section) => `${section.title}${section.name ? ` [${code(`help ${section.name}`)}]` : ''}`;
|
|
1040
|
+
function sectionLines(section, depth) {
|
|
1041
|
+
// A blank line after the heading before text, not before a list.
|
|
1042
|
+
const lines = [heading(sectionHeading(section), depth)];
|
|
1043
|
+
if (section.text)
|
|
1044
|
+
lines.push('', ...section.text.flatMap((text) => textLines(text, WIDTH)), '');
|
|
1045
|
+
for (const item of section.items ?? [])
|
|
1046
|
+
lines.push(...itemLines(item.term, item.about, 0));
|
|
1047
|
+
for (const command of section.commands ?? []) {
|
|
1048
|
+
if (command.gap)
|
|
1049
|
+
lines.push('');
|
|
1050
|
+
lines.push(...itemLines(command.usage, command.about, 0));
|
|
1051
|
+
for (const item of [...command.args ?? [], ...command.options ?? []])
|
|
1052
|
+
lines.push(...itemLines(item.term, item.about, 2));
|
|
1053
|
+
// Titles under a command, as `Lifetime:`, rather than headings,
|
|
1054
|
+
// which would end the command's entry.
|
|
1055
|
+
for (const group of command.optionGroups ?? []) {
|
|
1056
|
+
lines.push(` ${group.title}:`);
|
|
1057
|
+
for (const item of group.options)
|
|
1058
|
+
lines.push(...itemLines(item.term, item.about, 2));
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
if (section.options) {
|
|
1062
|
+
// Under a title when the section has commands, which they follow.
|
|
1063
|
+
if ((section.commands ?? []).length > 0)
|
|
1064
|
+
lines.push('', `${section.optionsTitle ?? 'Options'}:`);
|
|
1065
|
+
for (const item of section.options)
|
|
1066
|
+
lines.push(...itemLines(item.term, item.about, 0));
|
|
1067
|
+
}
|
|
1068
|
+
for (const sub of section.subsections ?? [])
|
|
1069
|
+
lines.push('', ...sectionLines(sub, depth + 1));
|
|
1070
|
+
return lines;
|
|
1071
|
+
}
|
|
1072
|
+
// The whole help, `sm-dev help all`.
|
|
1073
|
+
function fullLines() {
|
|
1074
|
+
return [...INTRO, ...HELP_SECTIONS.flatMap((section) => ['', ...sectionLines(section, 0)])];
|
|
1075
|
+
}
|
|
1076
|
+
// Every section, subsections included, in the order the help lists them.
|
|
1077
|
+
function allSections(sections = HELP_SECTIONS) {
|
|
1078
|
+
return sections.flatMap((section) => [section, ...allSections(section.subsections ?? [])]);
|
|
1079
|
+
}
|
|
1080
|
+
const nameOf = (command) => command.usage.split(' ')[0];
|
|
1081
|
+
// `sm-dev help`: what help can show, and each section's topic.
|
|
1082
|
+
function indexLines() {
|
|
1083
|
+
const rows = [];
|
|
1084
|
+
const add = (sections, depth) => {
|
|
1085
|
+
for (const section of sections) {
|
|
1086
|
+
if (section.name)
|
|
1087
|
+
rows.push([`${' '.repeat(depth * 2)}- ${quiet(section.name)}`, section.summary ?? section.title]);
|
|
1088
|
+
add(section.subsections ?? [], depth + 1);
|
|
1089
|
+
}
|
|
1090
|
+
};
|
|
1091
|
+
add(HELP_SECTIONS, 0);
|
|
1092
|
+
const usage = [
|
|
1093
|
+
['help <topic>', 'Help for a topic'],
|
|
1094
|
+
['help <command> [subcommand]', 'Help for a command, or one of its subcommands'],
|
|
1095
|
+
['help commands', 'Short list of all commands'],
|
|
1096
|
+
['help all', 'All topics'],
|
|
1097
|
+
['<command> [subcommand] --help', 'Help for a command, or one of its subcommands'],
|
|
1098
|
+
].map(([term, about]) => [`- ${quiet(term)}`, about]);
|
|
1099
|
+
// One column for both lists, so their descriptions line up, sized by
|
|
1100
|
+
// the topics: a longer help row has its description on the next line,
|
|
1101
|
+
// rather than pushing every topic's far from its name.
|
|
1102
|
+
const column = Math.max(...rows.map(([lead]) => visibleLength(lead))) + 2;
|
|
1103
|
+
const table = (list) => list.flatMap(([lead, about]) => {
|
|
1104
|
+
const [first, ...rest] = wrap(about, WIDTH - column);
|
|
1105
|
+
const indented = rest.map((line) => ' '.repeat(column) + line);
|
|
1106
|
+
if (visibleLength(lead) + 2 > column)
|
|
1107
|
+
return [lead, ' '.repeat(column) + first, ...indented];
|
|
1108
|
+
return [padTo(lead, column) + first, ...indented];
|
|
1109
|
+
});
|
|
1110
|
+
return [...INTRO, '', heading('Help', 0), ...table(usage), '', heading('Topics', 0), ...table(rows)];
|
|
1111
|
+
}
|
|
1112
|
+
// A command's line in `help commands`: its summary, or what it does up
|
|
1113
|
+
// to the first sentence's end.
|
|
1114
|
+
function summaryOf(command) {
|
|
1115
|
+
if (command.summary)
|
|
1116
|
+
return command.summary;
|
|
1117
|
+
const about = typeof command.about === 'string' ? command.about : command.about[0] ?? '';
|
|
1118
|
+
return /^.*?\.(?=\s|$)/.exec(about)?.[0] ?? about;
|
|
1119
|
+
}
|
|
1120
|
+
// `sm-dev help commands`: each command's name and a line on it, under its topics.
|
|
1121
|
+
function commandListLines() {
|
|
1122
|
+
const hasCommands = (section) => (section.commands ?? []).some((command) => !command.pointer) || (section.subsections ?? []).some(hasCommands);
|
|
1123
|
+
const lines = [`Commands, by topic. Use ${code('help <command>')} for one in full.`];
|
|
1124
|
+
// Just past the longest name, which are all short.
|
|
1125
|
+
const column = Math.max(...COMMAND_NAMES.map((name) => visibleLength(`- ${quiet(name)}`))) + 2;
|
|
1126
|
+
const add = (sections, depth) => {
|
|
1127
|
+
for (const section of sections.filter(hasCommands)) {
|
|
1128
|
+
lines.push('', heading(sectionHeading(section), depth));
|
|
1129
|
+
// A command of several forms (wait) once, by its first form's line.
|
|
1130
|
+
const seen = new Set();
|
|
1131
|
+
for (const command of section.commands ?? []) {
|
|
1132
|
+
const name = nameOf(command);
|
|
1133
|
+
if (command.pointer || seen.has(name))
|
|
1134
|
+
continue;
|
|
1135
|
+
seen.add(name);
|
|
1136
|
+
lines.push(...itemLines(name, summaryOf(command), 0, column, true));
|
|
1137
|
+
}
|
|
1138
|
+
add(section.subsections ?? [], depth + 1);
|
|
1139
|
+
}
|
|
1140
|
+
};
|
|
1141
|
+
add(HELP_SECTIONS, 0);
|
|
1142
|
+
return lines;
|
|
1143
|
+
}
|
|
1144
|
+
// The section `help <topic>` shows, or null for none. Null too for a
|
|
1145
|
+
// section that is just the command of that name (frames), whose own
|
|
1146
|
+
// help has all its options, and the section's text.
|
|
1147
|
+
function sectionTopic(topic) {
|
|
1148
|
+
const section = allSections().find((s) => s.name === topic);
|
|
1149
|
+
if (!section)
|
|
1150
|
+
return null;
|
|
1151
|
+
const command = commandNamed(topic);
|
|
1152
|
+
const own = (section.commands ?? []).filter((c) => !c.pointer).map(nameOf);
|
|
1153
|
+
if (command && !section.subsections?.length && own.every((name) => name === command))
|
|
1154
|
+
return null;
|
|
1155
|
+
return section;
|
|
1156
|
+
}
|
|
1157
|
+
/**
|
|
1158
|
+
* `sm-dev help [topic|command [subcommand]]`'s words, as what they ask
|
|
1159
|
+
* for: none for the index. An error for a name that is no topic or
|
|
1160
|
+
* command, a subcommand the command doesn't have, or more words.
|
|
1161
|
+
*/
|
|
1162
|
+
export function helpTarget(words) {
|
|
1163
|
+
if (words.length === 0)
|
|
1164
|
+
return null;
|
|
1165
|
+
const [first, second] = words;
|
|
1166
|
+
const takes = `The ${quoted('help')} command takes a topic, or a command and one of its subcommands: ${quoted('help script save')}`;
|
|
1167
|
+
if (words.length > 2)
|
|
1168
|
+
return { error: takes };
|
|
1169
|
+
const topic = first === 'full' ? 'all' : first;
|
|
1170
|
+
const command = commandNamed(first);
|
|
1171
|
+
if (second === undefined) {
|
|
1172
|
+
if (topic === 'all' || topic === 'commands' || sectionTopic(topic))
|
|
1173
|
+
return { topic };
|
|
1174
|
+
if (command)
|
|
1175
|
+
return { command };
|
|
1176
|
+
return { error: `${quoted(first)} is not a command or help topic. Use ${quoted('sm-dev help')} to list the topics, or ${quoted('sm-dev help commands')} for the commands.` };
|
|
1177
|
+
}
|
|
1178
|
+
if (!command) {
|
|
1179
|
+
if (topic === 'all' || topic === 'commands' || sectionTopic(topic))
|
|
1180
|
+
return { error: takes };
|
|
1181
|
+
return { error: `${quoted(first)} is not a command. Use ${quoted('sm-dev help commands')} to list the commands.` };
|
|
1182
|
+
}
|
|
1183
|
+
const missing = noSuchSubcommand(command, second);
|
|
1184
|
+
return missing ? { error: missing } : { command, subcommand: second };
|
|
1185
|
+
}
|
|
1186
|
+
/** `sm-dev help`'s text for `target` (the index for null), in color for a terminal that has it. */
|
|
1187
|
+
export function helpText(target, color = false) {
|
|
1188
|
+
return render(color, () => {
|
|
1189
|
+
if (target === null)
|
|
1190
|
+
return indexLines();
|
|
1191
|
+
if ('command' in target)
|
|
1192
|
+
return commandBlock(target.command, target.subcommand);
|
|
1193
|
+
if (target.topic === 'all')
|
|
1194
|
+
return fullLines();
|
|
1195
|
+
if (target.topic === 'commands')
|
|
1196
|
+
return commandListLines();
|
|
1197
|
+
return sectionLines(sectionTopic(target.topic), 0);
|
|
1198
|
+
});
|
|
1199
|
+
}
|
|
1200
|
+
/**
|
|
1201
|
+
* One command's help, or one subcommand's (`save`), or with `''`, its
|
|
1202
|
+
* forms with none (`script <script>`), in color for a terminal that has
|
|
1203
|
+
* it. For `<command> --help`, which always means the command, never a
|
|
1204
|
+
* topic of the same name.
|
|
1205
|
+
*/
|
|
1206
|
+
export function commandHelp(name, subcommand, color = false) {
|
|
1207
|
+
return render(color, () => commandBlock(commandNamed(name) ?? name, subcommand));
|
|
1208
|
+
}
|
|
1209
|
+
// Each command's forms (wait has several), with the section listing them.
|
|
1210
|
+
function formsOf(name) {
|
|
1211
|
+
const all = allSections().flatMap((section) => (section.commands ?? [])
|
|
1212
|
+
.filter((command) => nameOf(command) === name)
|
|
1213
|
+
.map((command) => ({ command, section })));
|
|
1214
|
+
// A pointer only when there is nothing else to show.
|
|
1215
|
+
const real = all.filter(({ command }) => !command.pointer);
|
|
1216
|
+
return real.length > 0 ? real : all;
|
|
1217
|
+
}
|
|
1218
|
+
/** The managers `name` works with: as its help says, else every one. */
|
|
1219
|
+
export function commandManagers(name) {
|
|
1220
|
+
return formsOf(name).map(({ command }) => command.managers).find((m) => m !== undefined) ?? MANAGER_NAMES;
|
|
1221
|
+
}
|
|
1222
|
+
/**
|
|
1223
|
+
* Why `name` can't be used with `manager`, or null when it can. `whose`
|
|
1224
|
+
* says where the manager came from: `this run uses`, `--manager names`.
|
|
1225
|
+
*/
|
|
1226
|
+
export function unsupportedCommand(name, manager, whose) {
|
|
1227
|
+
const managers = commandManagers(name);
|
|
1228
|
+
if (managers.includes(manager))
|
|
1229
|
+
return null;
|
|
1230
|
+
return `The ${quoted(name)} command works with ${managers.map((m) => MANAGERS[m].label).join(' or ')} only, and ${whose} ${MANAGERS[manager].label}`;
|
|
1231
|
+
}
|
|
1232
|
+
/** Every command's name, in the list's order; one with several forms once. */
|
|
1233
|
+
export const COMMAND_NAMES = [...new Set(allSections().flatMap((section) => (section.commands ?? []).filter((command) => !command.pointer).map(nameOf)))];
|
|
1234
|
+
/** The commands sm-dev runs itself, never sent to a run. */
|
|
1235
|
+
export const CLIENT_COMMANDS = new Set(['start', 'attach', 'stop', 'list', 'exit', 'help']);
|
|
1236
|
+
/**
|
|
1237
|
+
* The commands sm-dev runs itself when given only files and URLs, and
|
|
1238
|
+
* sends to a run for its scripts and tabs. Both lists name them.
|
|
1239
|
+
*/
|
|
1240
|
+
export const SHARED_COMMANDS = new Set(['match', 'validate']);
|
|
1241
|
+
/** The commands sm-dev can run itself: its own and the shared ones. */
|
|
1242
|
+
export const OWN_COMMANDS = new Set([...CLIENT_COMMANDS, ...SHARED_COMMANDS]);
|
|
1243
|
+
/** The commands a run takes. */
|
|
1244
|
+
export const RUN_COMMAND_NAMES = COMMAND_NAMES.filter((name) => !CLIENT_COMMANDS.has(name));
|
|
1245
|
+
/** The names a command also answers to, when run and in `help`. */
|
|
1246
|
+
export const ALIASES = { '?': 'help', keys: 'key', collections: 'collection' };
|
|
1247
|
+
/**
|
|
1248
|
+
* Singular and plural names that are one command (`script 2` picks as
|
|
1249
|
+
* `scripts 2` does), whose help is shown together, under the plural.
|
|
1250
|
+
*/
|
|
1251
|
+
export const SAME_COMMANDS = { script: 'scripts', tab: 'tabs' };
|
|
1252
|
+
/** A typed name as the command it is, aliases resolved; null for none. */
|
|
1253
|
+
export function commandNamed(typed) {
|
|
1254
|
+
const name = ALIASES[typed] ?? typed;
|
|
1255
|
+
return COMMAND_NAMES.includes(name) ? name : null;
|
|
1256
|
+
}
|
|
1257
|
+
// The keyword naming a subcommand (`wait log`, `script [script] save`),
|
|
1258
|
+
// or '' for a form with none (`wait <time>`). Only a form with a syntax
|
|
1259
|
+
// of its own is a subcommand: `help commands` is a topic.
|
|
1260
|
+
function keywordOf(command) {
|
|
1261
|
+
const named = /^\S+ (?:(<[^>]+>|\[[^\]]+\]) )?([a-z][a-z-]*)(\s|$)/.exec(command.usage);
|
|
1262
|
+
return named && command.syntax ? named[2] : '';
|
|
1263
|
+
}
|
|
1264
|
+
// The names whose forms a command's help shows: its own, and those of
|
|
1265
|
+
// its singular (`scripts` with `script`).
|
|
1266
|
+
function namesOf(name) {
|
|
1267
|
+
return [name, ...Object.keys(SAME_COMMANDS).filter((single) => SAME_COMMANDS[single] === name)];
|
|
1268
|
+
}
|
|
1269
|
+
/** A command's subcommands (`list`, `show`, ... for `script`), in its help's order. */
|
|
1270
|
+
export function subcommandsOf(name) {
|
|
1271
|
+
return [...new Set(namesOf(SAME_COMMANDS[name] ?? name).flatMap(formsOf).map(({ command }) => keywordOf(command)).filter(Boolean))];
|
|
1272
|
+
}
|
|
1273
|
+
/**
|
|
1274
|
+
* Said for a subcommand `name` doesn't have, or null when it has it.
|
|
1275
|
+
* `''` names its forms with no subcommand (`script <script>`).
|
|
1276
|
+
*/
|
|
1277
|
+
export function noSuchSubcommand(name, subcommand) {
|
|
1278
|
+
const subs = subcommandsOf(name);
|
|
1279
|
+
if (subcommand === '' ? hasPlainForm(name) : subs.includes(subcommand))
|
|
1280
|
+
return null;
|
|
1281
|
+
if (subs.length === 0)
|
|
1282
|
+
return `The ${quoted(name)} command has no subcommands`;
|
|
1283
|
+
return `The ${quoted(name)} command has no ${quoted(subcommand)} subcommand. It has ${subs.map((sub) => quoted(sub)).join(', ')}.`;
|
|
1284
|
+
}
|
|
1285
|
+
/**
|
|
1286
|
+
* The words a command's forms with no subcommand spell out as they are
|
|
1287
|
+
* (`all` in `wait [T<n>|all]`), which are arguments, not subcommands.
|
|
1288
|
+
*/
|
|
1289
|
+
export function plainFormWords(name) {
|
|
1290
|
+
return namesOf(SAME_COMMANDS[name] ?? name).flatMap(formsOf).filter(({ command }) => keywordOf(command) === '')
|
|
1291
|
+
.flatMap(({ command }) => command.usage.split(' ').slice(1).join(' ').replace(/<[^>]*>/g, '').match(/[a-z][a-z-]*/g) ?? []);
|
|
1292
|
+
}
|
|
1293
|
+
/** Whether a command has a form with no subcommand (`script <script>`, `wait <time>`), as opposed to only subcommands (`manager`). */
|
|
1294
|
+
export function hasPlainForm(name) {
|
|
1295
|
+
return namesOf(SAME_COMMANDS[name] ?? name).flatMap(formsOf).some(({ command }) => keywordOf(command) === '');
|
|
1296
|
+
}
|
|
1297
|
+
// Whether a shared option (listed in a section) is for `name`'s
|
|
1298
|
+
// subcommand `subcommand`: listed for the command, or for that
|
|
1299
|
+
// subcommand alone. Undefined `subcommand`: any of them.
|
|
1300
|
+
function sharedFor(item, name, subcommand) {
|
|
1301
|
+
return (item.commands ?? []).some((c) => c === name || (c.startsWith(`${name} `) && (subcommand === undefined || c === `${name} ${subcommand}`)));
|
|
1302
|
+
}
|
|
1303
|
+
// A command's shared options in titled groups: its own section's, then
|
|
1304
|
+
// those of any other section that lists some for it. `subcommand`:
|
|
1305
|
+
// only those it takes.
|
|
1306
|
+
function sharedGroupsOf(name, subcommand) {
|
|
1307
|
+
const home = formsOf(name)[0]?.section;
|
|
1308
|
+
const groups = [];
|
|
1309
|
+
const shared = (section) => (section.options ?? []).filter((item) => sharedFor(item, name, subcommand));
|
|
1310
|
+
if (home && shared(home).length > 0)
|
|
1311
|
+
groups.push({ title: 'Options', options: shared(home) });
|
|
1312
|
+
for (const section of allSections()) {
|
|
1313
|
+
if (section === home)
|
|
1314
|
+
continue;
|
|
1315
|
+
const options = shared(section);
|
|
1316
|
+
if (options.length > 0)
|
|
1317
|
+
groups.push({ title: section.title, options });
|
|
1318
|
+
}
|
|
1319
|
+
return groups;
|
|
1320
|
+
}
|
|
1321
|
+
// A form's own options, in titled groups.
|
|
1322
|
+
function ownGroupsOf(command) {
|
|
1323
|
+
return [...(command.options?.length ? [{ title: 'Options', options: command.options }] : []), ...command.optionGroups ?? []];
|
|
1324
|
+
}
|
|
1325
|
+
// Groups with the same title made one, each option once.
|
|
1326
|
+
function mergedGroups(groups) {
|
|
1327
|
+
const merged = [];
|
|
1328
|
+
for (const group of groups) {
|
|
1329
|
+
const same = merged.find((g) => g.title === group.title);
|
|
1330
|
+
if (!same)
|
|
1331
|
+
merged.push({ title: group.title, options: [...group.options] });
|
|
1332
|
+
else
|
|
1333
|
+
for (const item of group.options)
|
|
1334
|
+
if (!same.options.some((o) => o.term === item.term))
|
|
1335
|
+
same.options.push(item);
|
|
1336
|
+
}
|
|
1337
|
+
return merged;
|
|
1338
|
+
}
|
|
1339
|
+
/**
|
|
1340
|
+
* The flags a command takes, for checking a sent command's words: the
|
|
1341
|
+
* first word of each option's term.
|
|
1342
|
+
*/
|
|
1343
|
+
export function commandFlags(name) {
|
|
1344
|
+
return Object.keys(commandSyntax(name).flags);
|
|
1345
|
+
}
|
|
1346
|
+
// An option's flag, how it takes a value, and what the value is:
|
|
1347
|
+
// `--out FILE`, `--visible`, `--script[=<script>]`. Null for a term
|
|
1348
|
+
// that is no flag (`<frame>`).
|
|
1349
|
+
function termFlag(term) {
|
|
1350
|
+
const optional = /^(--[\w-]+)\[=([^\]]+)\]$/.exec(term);
|
|
1351
|
+
if (optional)
|
|
1352
|
+
return { flag: optional[1], kind: 'optional', value: optional[2] };
|
|
1353
|
+
const [flag, value] = term.split(/\s+/);
|
|
1354
|
+
if (!flag.startsWith('--'))
|
|
1355
|
+
return null;
|
|
1356
|
+
return { flag: flag.split(',')[0], kind: value ? 'value' : 'bool', ...(value ? { value } : {}) };
|
|
1357
|
+
}
|
|
1358
|
+
/** A command's syntax, from its help; `words` and no flags for a name that is none. */
|
|
1359
|
+
export function commandSyntax(name) {
|
|
1360
|
+
const forms = formsOf(name);
|
|
1361
|
+
const syntax = {
|
|
1362
|
+
base: WORDS, forms: {}, subjectForms: new Set(), flags: {}, formFlags: { '': [] }, values: {}, usage: forms.map(({ command }) => command.usage),
|
|
1363
|
+
};
|
|
1364
|
+
const take = (term, keywords) => {
|
|
1365
|
+
const found = termFlag(term);
|
|
1366
|
+
if (!found)
|
|
1367
|
+
return;
|
|
1368
|
+
const { flag, kind, value } = found;
|
|
1369
|
+
syntax.flags[flag] ??= kind;
|
|
1370
|
+
// One listed twice (`--tail N`, `--tail PERIOD`) names both: N|PERIOD.
|
|
1371
|
+
if (value)
|
|
1372
|
+
syntax.values[flag] = syntax.values[flag] && syntax.values[flag] !== value ? `${syntax.values[flag]}|${value}` : value;
|
|
1373
|
+
for (const keyword of keywords) {
|
|
1374
|
+
const list = (syntax.formFlags[keyword] ??= []);
|
|
1375
|
+
if (!list.includes(flag))
|
|
1376
|
+
list.push(flag);
|
|
1377
|
+
}
|
|
1378
|
+
};
|
|
1379
|
+
for (const { command } of forms) {
|
|
1380
|
+
// A form named by a keyword (`wait log <text>`), perhaps after what
|
|
1381
|
+
// it acts on (`script [script] enable`), or the command's own.
|
|
1382
|
+
const keyword = keywordOf(command);
|
|
1383
|
+
if (keyword) {
|
|
1384
|
+
syntax.forms[keyword] = command.syntax;
|
|
1385
|
+
if (/^\S+ (<[^>]+>|\[[^\]]+\]) /.test(command.usage))
|
|
1386
|
+
syntax.subjectForms.add(keyword);
|
|
1387
|
+
}
|
|
1388
|
+
else if (command.syntax)
|
|
1389
|
+
syntax.base = command.syntax;
|
|
1390
|
+
syntax.formFlags[keyword] ??= [];
|
|
1391
|
+
}
|
|
1392
|
+
const keywords = Object.keys(syntax.formFlags);
|
|
1393
|
+
// Its forms' own options, then the shared ones, each for the forms it is for.
|
|
1394
|
+
for (const { command } of forms) {
|
|
1395
|
+
for (const group of ownGroupsOf(command))
|
|
1396
|
+
for (const item of group.options)
|
|
1397
|
+
take(item.term, [keywordOf(command)]);
|
|
1398
|
+
}
|
|
1399
|
+
for (const group of sharedGroupsOf(name)) {
|
|
1400
|
+
for (const item of group.options)
|
|
1401
|
+
take(item.term, keywords.filter((k) => sharedFor(item, name, k)));
|
|
1402
|
+
}
|
|
1403
|
+
// The flags in a form's usage line (`script install ... [--force]`).
|
|
1404
|
+
for (const { command } of forms) {
|
|
1405
|
+
for (const [, flag, value] of command.usage.matchAll(/(--[\w-]+)(?:\s+(<[^>]+>))?/g))
|
|
1406
|
+
take(value ? `${flag} ${value}` : flag, [keywordOf(command)]);
|
|
1407
|
+
}
|
|
1408
|
+
return syntax;
|
|
1409
|
+
}
|
|
1410
|
+
// The flags for the whole line, which go before any command.
|
|
1411
|
+
export const GLOBAL_FLAGS = ['--run', '--root', '--local', '--stdin', '--interactive', '--temp-browser', '--open-browser', '--existing-browser', '--browser', '--manager', '--profile', '--headed', '--headless', '--mute', '--nomute', '--json'];
|
|
1412
|
+
// Those of them that take a value.
|
|
1413
|
+
export const VALUE_GLOBALS = new Set(['--run', '--root', '--local', '--browser', '--manager', '--profile']);
|
|
1414
|
+
// A global flag given where a command's own go.
|
|
1415
|
+
export function globalFlagMistake(flag) {
|
|
1416
|
+
return `The ${quoted(flag)} flag is for the whole command line, so it goes first, before any command: ${quoted(`sm-dev ${flag}${VALUE_GLOBALS.has(flag) ? ' ...' : ''} <command>`)}`;
|
|
1417
|
+
}
|
|
1418
|
+
/**
|
|
1419
|
+
* A command's flag it doesn't have, naming the ones it does, so a near
|
|
1420
|
+
* miss (`--timeout` for `--timeout-seconds`) shows the right one.
|
|
1421
|
+
* `command` can be a subcommand (`script save`).
|
|
1422
|
+
*/
|
|
1423
|
+
export function noSuchFlag(command, flag, known = commandFlags(command)) {
|
|
1424
|
+
const takes = known.length === 0 ? 'It takes no flags.' : `It takes ${known.map((f) => quoted(f)).join(', ')}.`;
|
|
1425
|
+
return `The ${quoted(command)} command has no ${quoted(flag)} flag. ${takes}`;
|
|
1426
|
+
}
|
|
1427
|
+
/**
|
|
1428
|
+
* A command's help: its usage, what it does, its arguments and options,
|
|
1429
|
+
* with the forms of its singular too (`script` with `scripts`).
|
|
1430
|
+
* `subcommand`: only that one's forms and the options it takes; `''`
|
|
1431
|
+
* for the forms with none (`script <script>`).
|
|
1432
|
+
*/
|
|
1433
|
+
function commandBlock(name, subcommand) {
|
|
1434
|
+
const names = namesOf(SAME_COMMANDS[name] ?? name);
|
|
1435
|
+
const forms = names.flatMap(formsOf).filter(({ command }) => subcommand === undefined || keywordOf(command) === subcommand);
|
|
1436
|
+
const shared = mergedGroups(names.flatMap((n) => sharedGroupsOf(n, subcommand)));
|
|
1437
|
+
// Each form, and a row after one that names no command
|
|
1438
|
+
// (`<command> --help`, after help's).
|
|
1439
|
+
const rows = forms.flatMap(({ command, section }) => {
|
|
1440
|
+
const next = section.commands?.[section.commands.indexOf(command) + 1];
|
|
1441
|
+
return subcommand === undefined && next?.pointer && !commandNamed(nameOf(next)) ? [command, next] : [command];
|
|
1442
|
+
});
|
|
1443
|
+
// One form's own options lead its groups; with several forms, each
|
|
1444
|
+
// form's are listed under it.
|
|
1445
|
+
const groups = rows.length === 1 ? mergedGroups([...ownGroupsOf(rows[0]), ...shared]) : shared;
|
|
1446
|
+
// Said once, for a command of one form; several list their options below.
|
|
1447
|
+
const options = groups.length > 0 && rows.length === 1 ? ' [options]' : '';
|
|
1448
|
+
const lines = rows.map((command, i) => `${i === 0 ? 'Usage:' : ' '} ${quiet(`sm-dev ${command.usage}${options}`)}`);
|
|
1449
|
+
lines.push('');
|
|
1450
|
+
if (rows.length === 1)
|
|
1451
|
+
lines.push(...aboutLines(rows[0].about, WIDTH));
|
|
1452
|
+
else {
|
|
1453
|
+
for (const command of rows) {
|
|
1454
|
+
lines.push(...itemLines(command.usage, command.about, 0));
|
|
1455
|
+
for (const group of ownGroupsOf(command)) {
|
|
1456
|
+
if (group.title !== 'Options')
|
|
1457
|
+
lines.push(` ${group.title}:`);
|
|
1458
|
+
for (const item of group.options)
|
|
1459
|
+
lines.push(...itemLines(item.term, item.about, 2));
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
}
|
|
1463
|
+
const args = forms.flatMap(({ command }) => command.args ?? []);
|
|
1464
|
+
const list = (title, items) => ['', `${title}:`, ...items.flatMap((item) => itemLines(item.term, item.about, 0))];
|
|
1465
|
+
if (args.length > 0)
|
|
1466
|
+
lines.push(...list('Arguments', args));
|
|
1467
|
+
for (const group of groups)
|
|
1468
|
+
lines.push(...list(group.title, group.options));
|
|
1469
|
+
const text = [...forms[0].section.text ?? []];
|
|
1470
|
+
while (text.at(-1) === '')
|
|
1471
|
+
text.pop();
|
|
1472
|
+
if (text.length > 0)
|
|
1473
|
+
lines.push('', ...text.flatMap((line) => textLines(line, WIDTH)));
|
|
1474
|
+
return lines;
|
|
1475
|
+
}
|