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,308 @@
|
|
|
1
|
+
// The eval hook: one statement sm-dev's scripts get at the top of their
|
|
2
|
+
// user program, so `sm-dev eval --script` can run code inside a
|
|
3
|
+
// script's own scope and see what it sees: its `GM_*` bindings, its
|
|
4
|
+
// `@require`s' top-level declarations, and its own.
|
|
5
|
+
//
|
|
6
|
+
// ;(globalThis[<key>] || <new map>)[<script id>] =
|
|
7
|
+
// ({ 'sm-dev eval': (code) => eval(code) })['sm-dev eval'];
|
|
8
|
+
//
|
|
9
|
+
// - A direct `eval` runs in the scope it's written in, so the arrow is a
|
|
10
|
+
// handle on the user program's scope that outlives the script's run.
|
|
11
|
+
// - Only scripts in an sm-dev run's test collection get it, never the
|
|
12
|
+
// person's installed scripts: the handle reaches everything the
|
|
13
|
+
// script can, `GM_*` included, and a MAIN-world script's is on the
|
|
14
|
+
// page's `window`. Its scope includes `__smReg`, the world's registry
|
|
15
|
+
// (src/extension/channel.ts), so it reaches every page-world script's
|
|
16
|
+
// GM runtime there, not just its own.
|
|
17
|
+
// - The key names the run and the map isn't enumerable, so it stays out
|
|
18
|
+
// of a page's casual view. A page that looks for it can still find it.
|
|
19
|
+
// - It needs the script not to shadow `globalThis`, `Object` or `eval`
|
|
20
|
+
// where it sits.
|
|
21
|
+
// - A script whose code opens with an IIFE (immediately invoked
|
|
22
|
+
// function expression), the usual `(function () { ... })()` wrapper,
|
|
23
|
+
// gets a second copy just inside it. That one replaces the first in
|
|
24
|
+
// the map as the IIFE starts, and sees the IIFE's locals as well:
|
|
25
|
+
// an inner scope sees everything the outer one does.
|
|
26
|
+
//
|
|
27
|
+
// Each copy goes after its scope's directive prologue (`"use strict"`):
|
|
28
|
+
// a statement ahead of a directive turns it into a plain string, and
|
|
29
|
+
// the code would run sloppy. On the same line, so no line moves.
|
|
30
|
+
// The arrow's parameter, which the evaluated code sees too: named so no
|
|
31
|
+
// script's own would be shadowed by it.
|
|
32
|
+
const CODE_PARAM = '__smDevEvalCode';
|
|
33
|
+
// The arrow's name, which its stack frame shows. Assigned straight to
|
|
34
|
+
// the map, it has none, and V8 names the frame after the map key, the
|
|
35
|
+
// script's id. Made under an object literal key, it takes that key.
|
|
36
|
+
const FRAME_NAME = JSON.stringify('sm-dev eval');
|
|
37
|
+
/** The global key a run's eval hooks are kept under. */
|
|
38
|
+
export function evalHookKey(run) {
|
|
39
|
+
return `__smDevEval:${run}`;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* `program` (requires + body, as composed) with the hook in it: at its
|
|
43
|
+
* top, and inside the script's IIFE when its own source, from
|
|
44
|
+
* `mainStart`, opens with one.
|
|
45
|
+
*/
|
|
46
|
+
export function withEvalHook(program, key, scriptId, mainStart = 0) {
|
|
47
|
+
const k = JSON.stringify(key);
|
|
48
|
+
const hook = `;(globalThis[${k}]||Object.defineProperty(globalThis,${k},{value:{}})[${k}])[${JSON.stringify(scriptId)}]=({${FRAME_NAME}:(${CODE_PARAM})=>eval(${CODE_PARAM})})[${FRAME_NAME}];`;
|
|
49
|
+
const outer = directivePrologueEnd(program);
|
|
50
|
+
const body = leadingFunctionBody(program, directivePrologueEnd(program, mainStart));
|
|
51
|
+
if (body < 0)
|
|
52
|
+
return program.slice(0, outer) + hook + program.slice(outer);
|
|
53
|
+
const inner = directivePrologueEnd(program, body);
|
|
54
|
+
return program.slice(0, outer) + hook + program.slice(outer, inner) + hook + program.slice(inner);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Where the body of the function expression the code at `from` opens
|
|
58
|
+
* with starts, just past its `{`: the IIFE in `(function () {`,
|
|
59
|
+
* `(() => {`, `(async function () {`, `!function () {` and the like.
|
|
60
|
+
* -1 when it opens with anything else, an arrow with an expression
|
|
61
|
+
* body included.
|
|
62
|
+
*
|
|
63
|
+
* Whether the function is called isn't checked: a copy of the hook in
|
|
64
|
+
* one that never runs is never registered, and the outer one stays.
|
|
65
|
+
* Nor is whether it's the only statement: a first IIFE's scope sees
|
|
66
|
+
* what follows it at the top level too.
|
|
67
|
+
*/
|
|
68
|
+
export function leadingFunctionBody(program, from) {
|
|
69
|
+
let i = skipSpace(program, from).at;
|
|
70
|
+
while (program[i] === ';')
|
|
71
|
+
i = skipSpace(program, i + 1).at;
|
|
72
|
+
if (/[!+\-~]/.test(program[i] ?? ''))
|
|
73
|
+
return functionBodyAt(program, skipSpace(program, i + 1).at, 0);
|
|
74
|
+
if (wordAt(program, i) === 'void')
|
|
75
|
+
return functionBodyAt(program, skipSpace(program, i + 4).at, 0);
|
|
76
|
+
// Without a prefix, only a parenthesis makes it an expression: a bare
|
|
77
|
+
// `function f() {` or `async function` is a declaration, a helper
|
|
78
|
+
// whose scope would shadow the script's names.
|
|
79
|
+
return program[i] === '(' ? functionBodyAt(program, i, 0) : -1;
|
|
80
|
+
}
|
|
81
|
+
// The body start of a function expression at `i`, through grouping
|
|
82
|
+
// parentheses, or -1.
|
|
83
|
+
function functionBodyAt(program, at, depth) {
|
|
84
|
+
let i = skipSpace(program, at).at;
|
|
85
|
+
if (wordAt(program, i) === 'async')
|
|
86
|
+
i = skipSpace(program, i + 5).at;
|
|
87
|
+
const word = wordAt(program, i);
|
|
88
|
+
if (word === 'function') {
|
|
89
|
+
i = skipSpace(program, i + 8).at;
|
|
90
|
+
if (program[i] === '*')
|
|
91
|
+
i = skipSpace(program, i + 1).at;
|
|
92
|
+
const name = wordAt(program, i);
|
|
93
|
+
if (name)
|
|
94
|
+
i = skipSpace(program, i + name.length).at;
|
|
95
|
+
if (program[i] !== '(')
|
|
96
|
+
return -1;
|
|
97
|
+
const close = matchingClose(program, i);
|
|
98
|
+
if (close < 0)
|
|
99
|
+
return -1;
|
|
100
|
+
i = skipSpace(program, close + 1).at;
|
|
101
|
+
return program[i] === '{' ? i + 1 : -1;
|
|
102
|
+
}
|
|
103
|
+
if (word)
|
|
104
|
+
return arrowBody(program, i + word.length);
|
|
105
|
+
if (program[i] !== '(')
|
|
106
|
+
return -1;
|
|
107
|
+
// A grouping parenthesis, looked into first, or an arrow's parameters.
|
|
108
|
+
if (depth < 4) {
|
|
109
|
+
const inner = functionBodyAt(program, i + 1, depth + 1);
|
|
110
|
+
if (inner >= 0)
|
|
111
|
+
return inner;
|
|
112
|
+
}
|
|
113
|
+
const close = matchingClose(program, i);
|
|
114
|
+
return close < 0 ? -1 : arrowBody(program, close + 1);
|
|
115
|
+
}
|
|
116
|
+
// The body start of an arrow whose parameters end at `at`, or -1.
|
|
117
|
+
function arrowBody(program, at) {
|
|
118
|
+
const arrow = skipSpace(program, at);
|
|
119
|
+
if (arrow.newline || !program.startsWith('=>', arrow.at))
|
|
120
|
+
return -1;
|
|
121
|
+
const brace = skipSpace(program, arrow.at + 2).at;
|
|
122
|
+
return program[brace] === '{' ? brace + 1 : -1;
|
|
123
|
+
}
|
|
124
|
+
// The identifier or keyword starting at `i`, or ''.
|
|
125
|
+
function wordAt(text, i) {
|
|
126
|
+
return /^[A-Za-z_$][\w$]*/.exec(text.slice(i, i + 64))?.[0] ?? '';
|
|
127
|
+
}
|
|
128
|
+
// The closer matching the bracket at `open`, past strings, template
|
|
129
|
+
// literals, comments and regular expressions, or -1 when the text runs
|
|
130
|
+
// out or reads as something unexpected.
|
|
131
|
+
function matchingClose(text, open) {
|
|
132
|
+
const closers = [];
|
|
133
|
+
// Whether a `/` here starts a regular expression rather than divides.
|
|
134
|
+
let regexAllowed = true;
|
|
135
|
+
for (let i = open; i < text.length; i++) {
|
|
136
|
+
const c = text[i];
|
|
137
|
+
if (c === '(' || c === '[' || c === '{') {
|
|
138
|
+
closers.push(c === '(' ? ')' : c === '[' ? ']' : '}');
|
|
139
|
+
regexAllowed = true;
|
|
140
|
+
}
|
|
141
|
+
else if (c === ')' || c === ']' || c === '}') {
|
|
142
|
+
if (closers.pop() !== c)
|
|
143
|
+
return -1;
|
|
144
|
+
if (closers.length === 0)
|
|
145
|
+
return i;
|
|
146
|
+
regexAllowed = false;
|
|
147
|
+
}
|
|
148
|
+
else if (c === '"' || c === '\'') {
|
|
149
|
+
i = stringEnd(text, i);
|
|
150
|
+
if (i < 0)
|
|
151
|
+
return -1;
|
|
152
|
+
regexAllowed = false;
|
|
153
|
+
}
|
|
154
|
+
else if (c === '`') {
|
|
155
|
+
i = templateEnd(text, i);
|
|
156
|
+
if (i < 0)
|
|
157
|
+
return -1;
|
|
158
|
+
regexAllowed = false;
|
|
159
|
+
}
|
|
160
|
+
else if (c === '/' && (text[i + 1] === '/' || text[i + 1] === '*')) {
|
|
161
|
+
i = skipSpace(text, i).at - 1;
|
|
162
|
+
}
|
|
163
|
+
else if (c === '/' && regexAllowed) {
|
|
164
|
+
i = regexEnd(text, i);
|
|
165
|
+
if (i < 0)
|
|
166
|
+
return -1;
|
|
167
|
+
regexAllowed = false;
|
|
168
|
+
}
|
|
169
|
+
else if (/[\w$]/.test(c)) {
|
|
170
|
+
const word = wordAt(text, i) || c;
|
|
171
|
+
// After a keyword like `return`, an expression starts.
|
|
172
|
+
regexAllowed = /^(return|typeof|case|do|else|in|instanceof|new|delete|void|throw|yield|await)$/.test(word);
|
|
173
|
+
i += word.length - 1;
|
|
174
|
+
}
|
|
175
|
+
else if (!/\s/.test(c)) {
|
|
176
|
+
regexAllowed = true;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return -1;
|
|
180
|
+
}
|
|
181
|
+
// The index of the quote closing the string at `i`, or -1.
|
|
182
|
+
function stringEnd(text, i) {
|
|
183
|
+
const quote = text[i];
|
|
184
|
+
for (let j = i + 1; j < text.length; j++) {
|
|
185
|
+
if (text[j] === '\\')
|
|
186
|
+
j++;
|
|
187
|
+
else if (text[j] === quote)
|
|
188
|
+
return j;
|
|
189
|
+
else if (text[j] === '\n')
|
|
190
|
+
return -1;
|
|
191
|
+
}
|
|
192
|
+
return -1;
|
|
193
|
+
}
|
|
194
|
+
// The index of the backtick closing the template literal at `i`, its
|
|
195
|
+
// `${...}` parts skipped, or -1.
|
|
196
|
+
function templateEnd(text, i) {
|
|
197
|
+
for (let j = i + 1; j < text.length; j++) {
|
|
198
|
+
if (text[j] === '\\')
|
|
199
|
+
j++;
|
|
200
|
+
else if (text[j] === '`')
|
|
201
|
+
return j;
|
|
202
|
+
else if (text[j] === '$' && text[j + 1] === '{') {
|
|
203
|
+
j = matchingClose(text, j + 1);
|
|
204
|
+
if (j < 0)
|
|
205
|
+
return -1;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return -1;
|
|
209
|
+
}
|
|
210
|
+
// The index of the `/` closing the regular expression at `i`, or -1.
|
|
211
|
+
function regexEnd(text, i) {
|
|
212
|
+
let inClass = false;
|
|
213
|
+
for (let j = i + 1; j < text.length; j++) {
|
|
214
|
+
const c = text[j];
|
|
215
|
+
if (c === '\\')
|
|
216
|
+
j++;
|
|
217
|
+
else if (c === '\n' || c === '\r')
|
|
218
|
+
return -1;
|
|
219
|
+
else if (c === '[')
|
|
220
|
+
inClass = true;
|
|
221
|
+
else if (c === ']')
|
|
222
|
+
inClass = false;
|
|
223
|
+
else if (c === '/' && !inClass)
|
|
224
|
+
return j;
|
|
225
|
+
}
|
|
226
|
+
return -1;
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Where the directive prologue of the code at `from` ends (a program,
|
|
230
|
+
* or a function body): just past its last directive (a string literal
|
|
231
|
+
* statement at the start, `"use strict"` the one that matters), or
|
|
232
|
+
* `from` when it has none. Comments and whitespace before and between
|
|
233
|
+
* directives are skipped.
|
|
234
|
+
*
|
|
235
|
+
* A string that may be the start of a longer expression isn't counted
|
|
236
|
+
* as a directive: then the hook goes before it, which is right unless
|
|
237
|
+
* the guess was wrong, in code no one writes (`'use strict'` followed
|
|
238
|
+
* on the next line by `in x`).
|
|
239
|
+
*/
|
|
240
|
+
export function directivePrologueEnd(program, from = 0) {
|
|
241
|
+
let end = from;
|
|
242
|
+
let i = skipSpace(program, from).at;
|
|
243
|
+
for (;;) {
|
|
244
|
+
const quote = program[i];
|
|
245
|
+
if (quote !== '"' && quote !== '\'')
|
|
246
|
+
return end;
|
|
247
|
+
// The literal's end. Unclosed, or broken by a line break: not ours
|
|
248
|
+
// to judge, so no further.
|
|
249
|
+
let j = i + 1;
|
|
250
|
+
while (j < program.length && program[j] !== quote) {
|
|
251
|
+
if (program[j] === '\n' || program[j] === '\r')
|
|
252
|
+
return end;
|
|
253
|
+
j += program[j] === '\\' ? 2 : 1;
|
|
254
|
+
}
|
|
255
|
+
if (j >= program.length)
|
|
256
|
+
return end;
|
|
257
|
+
const literalEnd = j + 1;
|
|
258
|
+
const after = skipSpace(program, literalEnd);
|
|
259
|
+
const next = program[after.at];
|
|
260
|
+
if (next === ';') {
|
|
261
|
+
end = after.at + 1;
|
|
262
|
+
}
|
|
263
|
+
else if (next === undefined || next === '}' || (after.newline && !continuesExpression(program, after.at))) {
|
|
264
|
+
// Ended by the line break (automatic semicolon insertion), by the
|
|
265
|
+
// end of the program, or by the end of a function body.
|
|
266
|
+
end = literalEnd;
|
|
267
|
+
}
|
|
268
|
+
else {
|
|
269
|
+
return end;
|
|
270
|
+
}
|
|
271
|
+
i = skipSpace(program, end).at;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
// Past whitespace and comments from `at`, and whether a line break was
|
|
275
|
+
// among them (a `/* */` comment's included).
|
|
276
|
+
function skipSpace(text, at) {
|
|
277
|
+
let newline = false;
|
|
278
|
+
let i = at;
|
|
279
|
+
for (;;) {
|
|
280
|
+
const c = text[i];
|
|
281
|
+
if (c === '\n' || c === '\r' || c === '\u2028' || c === '\u2029') {
|
|
282
|
+
newline = true;
|
|
283
|
+
i++;
|
|
284
|
+
}
|
|
285
|
+
else if (c !== undefined && /\s/.test(c)) {
|
|
286
|
+
i++;
|
|
287
|
+
}
|
|
288
|
+
else if (c === '/' && text[i + 1] === '/') {
|
|
289
|
+
while (i < text.length && text[i] !== '\n' && text[i] !== '\r')
|
|
290
|
+
i++;
|
|
291
|
+
}
|
|
292
|
+
else if (c === '/' && text[i + 1] === '*') {
|
|
293
|
+
const close = text.indexOf('*/', i + 2);
|
|
294
|
+
const stop = close < 0 ? text.length : close + 2;
|
|
295
|
+
if (/[\n\r\u2028\u2029]/.test(text.slice(i, stop)))
|
|
296
|
+
newline = true;
|
|
297
|
+
i = stop;
|
|
298
|
+
}
|
|
299
|
+
else {
|
|
300
|
+
return { at: i, newline };
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
// Whether the token at `at` could carry on an expression from the line
|
|
305
|
+
// before, so no semicolon is inserted between them.
|
|
306
|
+
function continuesExpression(text, at) {
|
|
307
|
+
return /[[(.,?+\-*/%=<>&|^`]/.test(text[at]) || /^(in|instanceof)\b/.test(text.slice(at, at + 11));
|
|
308
|
+
}
|