@ogygia/devtools 0.0.0 → 0.11.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 +24 -1
- package/dist/client.d.ts +3 -0
- package/dist/client.js +24 -0
- package/dist/devtools/BoundaryOverlay.svelte +396 -0
- package/dist/devtools/BoundaryOverlay.svelte.d.ts +19 -0
- package/dist/devtools/Devtools.svelte +663 -0
- package/dist/devtools/Devtools.svelte.d.ts +15 -0
- package/dist/devtools/HubTab.svelte +206 -0
- package/dist/devtools/HubTab.svelte.d.ts +11 -0
- package/dist/devtools/HydrationTab.svelte +237 -0
- package/dist/devtools/HydrationTab.svelte.d.ts +13 -0
- package/dist/devtools/IslandDetail.svelte +615 -0
- package/dist/devtools/IslandDetail.svelte.d.ts +15 -0
- package/dist/devtools/LedgerTab.svelte +287 -0
- package/dist/devtools/LedgerTab.svelte.d.ts +11 -0
- package/dist/devtools/LensTab.svelte +290 -0
- package/dist/devtools/LensTab.svelte.d.ts +19 -0
- package/dist/devtools/NavTab.svelte +229 -0
- package/dist/devtools/NavTab.svelte.d.ts +11 -0
- package/dist/devtools/PageTab.svelte +1122 -0
- package/dist/devtools/PageTab.svelte.d.ts +15 -0
- package/dist/devtools/ProfilerTab.svelte +755 -0
- package/dist/devtools/ProfilerTab.svelte.d.ts +15 -0
- package/dist/devtools/PropsTree.svelte +155 -0
- package/dist/devtools/PropsTree.svelte.d.ts +15 -0
- package/dist/devtools/SessionTab.svelte +796 -0
- package/dist/devtools/SessionTab.svelte.d.ts +11 -0
- package/dist/devtools/TimelineTab.svelte +267 -0
- package/dist/devtools/TimelineTab.svelte.d.ts +11 -0
- package/dist/devtools/WireTab.svelte +149 -0
- package/dist/devtools/WireTab.svelte.d.ts +11 -0
- package/dist/devtools/bytes.d.ts +70 -0
- package/dist/devtools/bytes.js +82 -0
- package/dist/devtools/cache-headers.d.ts +20 -0
- package/dist/devtools/cache-headers.js +81 -0
- package/dist/devtools/highlight.d.ts +7 -0
- package/dist/devtools/highlight.js +83 -0
- package/dist/devtools/hydration.d.ts +48 -0
- package/dist/devtools/hydration.js +93 -0
- package/dist/devtools/index.d.ts +3 -0
- package/dist/devtools/index.js +3 -0
- package/dist/devtools/ledger-dom.d.ts +7 -0
- package/dist/devtools/ledger-dom.js +151 -0
- package/dist/devtools/leftovers.d.ts +77 -0
- package/dist/devtools/leftovers.js +319 -0
- package/dist/devtools/names.d.ts +1 -0
- package/dist/devtools/names.js +34 -0
- package/dist/devtools/page.d.ts +96 -0
- package/dist/devtools/page.js +957 -0
- package/dist/devtools/profile-anchors.d.ts +22 -0
- package/dist/devtools/profile-anchors.js +135 -0
- package/dist/devtools/profile-store.d.ts +175 -0
- package/dist/devtools/profile-store.js +239 -0
- package/dist/devtools/regions.d.ts +77 -0
- package/dist/devtools/regions.js +132 -0
- package/dist/devtools/session.d.ts +31 -0
- package/dist/devtools/session.js +455 -0
- package/dist/devtools/since-load.d.ts +50 -0
- package/dist/devtools/since-load.js +89 -0
- package/dist/devtools/source-lines.d.ts +24 -0
- package/dist/devtools/source-lines.js +106 -0
- package/dist/devtools/styles.d.ts +52 -0
- package/dist/devtools/styles.js +248 -0
- package/dist/devtools/testing-engine.d.ts +83 -0
- package/dist/devtools/testing-engine.js +220 -0
- package/dist/devtools/ui-app.d.ts +5 -0
- package/dist/devtools/ui-app.js +69 -0
- package/dist/devtools/ui.d.ts +50 -0
- package/dist/devtools/ui.js +175 -0
- package/dist/profiler/alloc.d.ts +75 -0
- package/dist/profiler/alloc.js +76 -0
- package/dist/profiler/analyze.d.ts +437 -0
- package/dist/profiler/analyze.js +1941 -0
- package/dist/profiler/app-path.d.ts +7 -0
- package/dist/profiler/app-path.js +26 -0
- package/dist/profiler/async-io.d.ts +58 -0
- package/dist/profiler/async-io.js +187 -0
- package/dist/profiler/beacon-standalone.d.ts +4 -0
- package/dist/profiler/beacon-standalone.js +409 -0
- package/dist/profiler/browser-findings.d.ts +25 -0
- package/dist/profiler/browser-findings.js +260 -0
- package/dist/profiler/byte-strip.d.ts +58 -0
- package/dist/profiler/byte-strip.js +228 -0
- package/dist/profiler/client-files.d.ts +16 -0
- package/dist/profiler/client-files.js +60 -0
- package/dist/profiler/client-windows.d.ts +49 -0
- package/dist/profiler/client-windows.js +95 -0
- package/dist/profiler/client.d.ts +40 -0
- package/dist/profiler/client.js +82 -0
- package/dist/profiler/compare.d.ts +308 -0
- package/dist/profiler/compare.js +733 -0
- package/dist/profiler/compress.d.ts +10 -0
- package/dist/profiler/compress.js +36 -0
- package/dist/profiler/contention.d.ts +60 -0
- package/dist/profiler/contention.js +86 -0
- package/dist/profiler/crypto.d.ts +19 -0
- package/dist/profiler/crypto.js +101 -0
- package/dist/profiler/doc-diff.d.ts +47 -0
- package/dist/profiler/doc-diff.js +214 -0
- package/dist/profiler/dom-diff.d.ts +43 -0
- package/dist/profiler/dom-diff.js +121 -0
- package/dist/profiler/drill.d.ts +89 -0
- package/dist/profiler/drill.js +553 -0
- package/dist/profiler/embedded-maps.d.ts +37 -0
- package/dist/profiler/embedded-maps.js +135 -0
- package/dist/profiler/env.d.ts +6 -0
- package/dist/profiler/env.js +10 -0
- package/dist/profiler/forecast.d.ts +76 -0
- package/dist/profiler/forecast.js +225 -0
- package/dist/profiler/frames.d.ts +11 -0
- package/dist/profiler/frames.js +42 -0
- package/dist/profiler/gc.d.ts +144 -0
- package/dist/profiler/gc.js +271 -0
- package/dist/profiler/hole-slots.d.ts +31 -0
- package/dist/profiler/hole-slots.js +90 -0
- package/dist/profiler/index.d.ts +288 -0
- package/dist/profiler/index.js +4921 -0
- package/dist/profiler/insights.d.ts +96 -0
- package/dist/profiler/insights.js +239 -0
- package/dist/profiler/ledger.d.ts +121 -0
- package/dist/profiler/ledger.js +440 -0
- package/dist/profiler/lineage.d.ts +81 -0
- package/dist/profiler/lineage.js +359 -0
- package/dist/profiler/module-map.d.ts +12 -0
- package/dist/profiler/module-map.js +94 -0
- package/dist/profiler/net.d.ts +169 -0
- package/dist/profiler/net.js +586 -0
- package/dist/profiler/on-demand.d.ts +11 -0
- package/dist/profiler/on-demand.js +15 -0
- package/dist/profiler/own-requests.d.ts +9 -0
- package/dist/profiler/own-requests.js +17 -0
- package/dist/profiler/patterns.d.ts +476 -0
- package/dist/profiler/patterns.js +2203 -0
- package/dist/profiler/profiler-router.d.ts +245 -0
- package/dist/profiler/profiler-router.js +113 -0
- package/dist/profiler/refresh-stub.d.ts +2 -0
- package/dist/profiler/refresh-stub.js +37 -0
- package/dist/profiler/report.d.ts +1404 -0
- package/dist/profiler/report.js +2638 -0
- package/dist/profiler/river.d.ts +65 -0
- package/dist/profiler/river.js +298 -0
- package/dist/profiler/route-files.d.ts +22 -0
- package/dist/profiler/route-files.js +55 -0
- package/dist/profiler/route-imports.d.ts +14 -0
- package/dist/profiler/route-imports.js +83 -0
- package/dist/profiler/saving.d.ts +22 -0
- package/dist/profiler/saving.js +42 -0
- package/dist/profiler/score.d.ts +109 -0
- package/dist/profiler/score.js +261 -0
- package/dist/profiler/session-cookie.d.ts +28 -0
- package/dist/profiler/session-cookie.js +45 -0
- package/dist/profiler/sink.d.ts +81 -0
- package/dist/profiler/sink.js +124 -0
- package/dist/profiler/site-fixes.d.ts +25 -0
- package/dist/profiler/site-fixes.js +62 -0
- package/dist/profiler/source-scan.d.ts +210 -0
- package/dist/profiler/source-scan.js +1724 -0
- package/dist/profiler/span.d.ts +90 -0
- package/dist/profiler/span.js +109 -0
- package/dist/profiler/stacks.d.ts +43 -0
- package/dist/profiler/stacks.js +107 -0
- package/dist/profiler/standalone.d.ts +27 -0
- package/dist/profiler/standalone.js +98 -0
- package/dist/profiler/steps.d.ts +39 -0
- package/dist/profiler/steps.js +74 -0
- package/dist/profiler/storage/index.d.ts +87 -0
- package/dist/profiler/storage/index.js +76 -0
- package/dist/profiler/storage/postgres.d.ts +12 -0
- package/dist/profiler/storage/postgres.js +94 -0
- package/dist/profiler/storage/redis.d.ts +19 -0
- package/dist/profiler/storage/redis.js +148 -0
- package/dist/profiler/storage/sqlite.d.ts +2 -0
- package/dist/profiler/storage/sqlite.js +85 -0
- package/dist/profiler/storage/visits.d.ts +4 -0
- package/dist/profiler/storage/visits.js +6 -0
- package/dist/profiler/stream-tail.d.ts +26 -0
- package/dist/profiler/stream-tail.js +74 -0
- package/dist/profiler/timeline.d.ts +279 -0
- package/dist/profiler/timeline.js +690 -0
- package/dist/profiler/ui/AllocStrip.svelte +146 -0
- package/dist/profiler/ui/AllocStrip.svelte.d.ts +17 -0
- package/dist/profiler/ui/Awaits.svelte +363 -0
- package/dist/profiler/ui/Awaits.svelte.d.ts +16 -0
- package/dist/profiler/ui/ByteStrip.svelte +182 -0
- package/dist/profiler/ui/ByteStrip.svelte.d.ts +18 -0
- package/dist/profiler/ui/Compare.svelte +24 -0
- package/dist/profiler/ui/Compare.svelte.d.ts +16 -0
- package/dist/profiler/ui/CompareBody.svelte +429 -0
- package/dist/profiler/ui/CompareBody.svelte.d.ts +8 -0
- package/dist/profiler/ui/ComponentsTable.svelte +265 -0
- package/dist/profiler/ui/ComponentsTable.svelte.d.ts +20 -0
- package/dist/profiler/ui/Dashboard.svelte +492 -0
- package/dist/profiler/ui/Dashboard.svelte.d.ts +31 -0
- package/dist/profiler/ui/DataRiver.svelte +206 -0
- package/dist/profiler/ui/DataRiver.svelte.d.ts +13 -0
- package/dist/profiler/ui/DomTravel.svelte +207 -0
- package/dist/profiler/ui/DomTravel.svelte.d.ts +9 -0
- package/dist/profiler/ui/DownloadHtml.svelte +52 -0
- package/dist/profiler/ui/DownloadHtml.svelte.d.ts +10 -0
- package/dist/profiler/ui/Drill.svelte +276 -0
- package/dist/profiler/ui/Drill.svelte.d.ts +12 -0
- package/dist/profiler/ui/ExportButton.svelte +59 -0
- package/dist/profiler/ui/ExportButton.svelte.d.ts +8 -0
- package/dist/profiler/ui/Flame.svelte +222 -0
- package/dist/profiler/ui/Flame.svelte.d.ts +12 -0
- package/dist/profiler/ui/FrameDetails.svelte +308 -0
- package/dist/profiler/ui/FrameDetails.svelte.d.ts +16 -0
- package/dist/profiler/ui/FunctionsTable.svelte +156 -0
- package/dist/profiler/ui/FunctionsTable.svelte.d.ts +17 -0
- package/dist/profiler/ui/IslandHeatmap.svelte +151 -0
- package/dist/profiler/ui/IslandHeatmap.svelte.d.ts +16 -0
- package/dist/profiler/ui/IslandsTable.svelte +448 -0
- package/dist/profiler/ui/IslandsTable.svelte.d.ts +16 -0
- package/dist/profiler/ui/KeepLocal.svelte +46 -0
- package/dist/profiler/ui/KeepLocal.svelte.d.ts +8 -0
- package/dist/profiler/ui/Ledger.svelte +196 -0
- package/dist/profiler/ui/Ledger.svelte.d.ts +18 -0
- package/dist/profiler/ui/LocalCompare.svelte +48 -0
- package/dist/profiler/ui/LocalCompare.svelte.d.ts +8 -0
- package/dist/profiler/ui/LocalReports.svelte +68 -0
- package/dist/profiler/ui/LocalReports.svelte.d.ts +7 -0
- package/dist/profiler/ui/Login.svelte +13 -0
- package/dist/profiler/ui/Login.svelte.d.ts +12 -0
- package/dist/profiler/ui/LoginForm.svelte +42 -0
- package/dist/profiler/ui/LoginForm.svelte.d.ts +7 -0
- package/dist/profiler/ui/Message.svelte +10 -0
- package/dist/profiler/ui/Message.svelte.d.ts +12 -0
- package/dist/profiler/ui/OnThisPage.svelte +137 -0
- package/dist/profiler/ui/OnThisPage.svelte.d.ts +3 -0
- package/dist/profiler/ui/OneClock.svelte +297 -0
- package/dist/profiler/ui/OneClock.svelte.d.ts +27 -0
- package/dist/profiler/ui/Panel.svelte +10 -0
- package/dist/profiler/ui/Panel.svelte.d.ts +16 -0
- package/dist/profiler/ui/Paths.svelte +269 -0
- package/dist/profiler/ui/Paths.svelte.d.ts +15 -0
- package/dist/profiler/ui/Patterns.svelte +364 -0
- package/dist/profiler/ui/Patterns.svelte.d.ts +15 -0
- package/dist/profiler/ui/PermalinkGate.svelte +143 -0
- package/dist/profiler/ui/PermalinkGate.svelte.d.ts +10 -0
- package/dist/profiler/ui/RenderSteps.svelte +218 -0
- package/dist/profiler/ui/RenderSteps.svelte.d.ts +13 -0
- package/dist/profiler/ui/Report.svelte +28 -0
- package/dist/profiler/ui/Report.svelte.d.ts +20 -0
- package/dist/profiler/ui/ReportBody.svelte +2592 -0
- package/dist/profiler/ui/ReportBody.svelte.d.ts +21 -0
- package/dist/profiler/ui/Run.svelte +17 -0
- package/dist/profiler/ui/Run.svelte.d.ts +14 -0
- package/dist/profiler/ui/RunView.svelte +146 -0
- package/dist/profiler/ui/RunView.svelte.d.ts +10 -0
- package/dist/profiler/ui/ScoreCard.svelte +197 -0
- package/dist/profiler/ui/ScoreCard.svelte.d.ts +7 -0
- package/dist/profiler/ui/Scrub.svelte +293 -0
- package/dist/profiler/ui/Scrub.svelte.d.ts +18 -0
- package/dist/profiler/ui/SeedExplainer.svelte +193 -0
- package/dist/profiler/ui/SeedExplainer.svelte.d.ts +16 -0
- package/dist/profiler/ui/ShareLink.svelte +148 -0
- package/dist/profiler/ui/ShareLink.svelte.d.ts +8 -0
- package/dist/profiler/ui/Shell.svelte +50 -0
- package/dist/profiler/ui/Shell.svelte.d.ts +27 -0
- package/dist/profiler/ui/Sidebar.svelte +312 -0
- package/dist/profiler/ui/Sidebar.svelte.d.ts +7 -0
- package/dist/profiler/ui/Site.svelte +20 -0
- package/dist/profiler/ui/Site.svelte.d.ts +23 -0
- package/dist/profiler/ui/SiteView.svelte +385 -0
- package/dist/profiler/ui/SiteView.svelte.d.ts +27 -0
- package/dist/profiler/ui/ThemeSwitch.svelte +78 -0
- package/dist/profiler/ui/ThemeSwitch.svelte.d.ts +3 -0
- package/dist/profiler/ui/Timeline.svelte +672 -0
- package/dist/profiler/ui/Timeline.svelte.d.ts +22 -0
- package/dist/profiler/ui/Treemap.svelte +278 -0
- package/dist/profiler/ui/Treemap.svelte.d.ts +20 -0
- package/dist/profiler/ui/Upload.svelte +19 -0
- package/dist/profiler/ui/Upload.svelte.d.ts +10 -0
- package/dist/profiler/ui/UploadForm.svelte +59 -0
- package/dist/profiler/ui/UploadForm.svelte.d.ts +6 -0
- package/dist/profiler/ui/WaitingTable.svelte +55 -0
- package/dist/profiler/ui/WaitingTable.svelte.d.ts +14 -0
- package/dist/profiler/ui/Waterfall.svelte +371 -0
- package/dist/profiler/ui/Waterfall.svelte.d.ts +7 -0
- package/dist/profiler/ui/WhatIf.svelte +87 -0
- package/dist/profiler/ui/WhatIf.svelte.d.ts +18 -0
- package/dist/profiler/ui/fake-progress.svelte.d.ts +15 -0
- package/dist/profiler/ui/fake-progress.svelte.js +38 -0
- package/dist/profiler/ui/format.d.ts +20 -0
- package/dist/profiler/ui/format.js +71 -0
- package/dist/profiler/ui/permalink.d.ts +9 -0
- package/dist/profiler/ui/permalink.js +108 -0
- package/dist/profiler/ui/press.d.ts +5 -0
- package/dist/profiler/ui/press.js +19 -0
- package/dist/profiler/ui/profiler.css +650 -0
- package/dist/profiler/ui/report-data.d.ts +189 -0
- package/dist/profiler/ui/report-data.js +393 -0
- package/dist/profiler/ui/report-request.d.ts +6 -0
- package/dist/profiler/ui/report-request.js +50 -0
- package/dist/profiler/ui/row-anchor.svelte.d.ts +12 -0
- package/dist/profiler/ui/row-anchor.svelte.js +44 -0
- package/dist/profiler/ui/sort.svelte.d.ts +14 -0
- package/dist/profiler/ui/sort.svelte.js +37 -0
- package/dist/profiler/ui/store.d.ts +45 -0
- package/dist/profiler/ui/store.js +92 -0
- package/dist/profiler/visit.d.ts +389 -0
- package/dist/profiler/visit.js +1002 -0
- package/dist/report.d.ts +5 -0
- package/dist/report.js +4 -0
- package/dist/server.d.ts +11 -0
- package/dist/server.js +38 -0
- package/dist/vite.d.ts +12 -0
- package/dist/vite.js +78 -0
- package/package.json +105 -6
|
@@ -0,0 +1,1724 @@
|
|
|
1
|
+
// src/profiler/source-scan.ts
|
|
2
|
+
const is_id_start = (c) => c >= 65 && c <= 90 || c >= 97 && c <= 122 || c === 36 || c === 95 || c > 127;
|
|
3
|
+
const is_id_part = (c) => is_id_start(c) || c >= 48 && c <= 57;
|
|
4
|
+
const PUNCT2 = /* @__PURE__ */ new Set([
|
|
5
|
+
"=>",
|
|
6
|
+
"+=",
|
|
7
|
+
"?.",
|
|
8
|
+
"&&",
|
|
9
|
+
"||",
|
|
10
|
+
"??",
|
|
11
|
+
"==",
|
|
12
|
+
"!=",
|
|
13
|
+
"<=",
|
|
14
|
+
">=",
|
|
15
|
+
"++",
|
|
16
|
+
"--"
|
|
17
|
+
]);
|
|
18
|
+
/** One line of JS/TS as tokens: identifiers and keywords as they are, every string literal as
|
|
19
|
+
* `""`, every number as `0`, punctuation one token each (`...`, `=>`, `+=`, `?.` kept whole).
|
|
20
|
+
* Comments are dropped. A template literal is one string token followed by each `${…}` part's
|
|
21
|
+
* own tokens in parentheses (`\`t${i + k}\`` → `""`, `(`, `i`, `+`, `k`, `)`), so the code inside
|
|
22
|
+
* it is seen. */
|
|
23
|
+
function tokens(line) {
|
|
24
|
+
const out = [];
|
|
25
|
+
const n = line.length;
|
|
26
|
+
let i = 0;
|
|
27
|
+
while (i < n) {
|
|
28
|
+
const c = line.charCodeAt(i);
|
|
29
|
+
if (c === 32 || c === 9 || c === 13) {
|
|
30
|
+
i++;
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
if (c === 47 && line.charCodeAt(i + 1) === 47) break;
|
|
34
|
+
if (c === 47 && line.charCodeAt(i + 1) === 42) {
|
|
35
|
+
const e = line.indexOf("*/", i + 2);
|
|
36
|
+
if (e < 0) break;
|
|
37
|
+
i = e + 2;
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (c === 34 || c === 39 || c === 96) {
|
|
41
|
+
let j = i + 1;
|
|
42
|
+
/** a template literal's `${…}` parts: code, tokenized after the string's own token */
|
|
43
|
+
const parts = [];
|
|
44
|
+
while (j < n) {
|
|
45
|
+
const d = line.charCodeAt(j);
|
|
46
|
+
if (d === 92) {
|
|
47
|
+
j += 2;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (d === c) break;
|
|
51
|
+
if (c === 96 && d === 36 && line.charCodeAt(j + 1) === 123) {
|
|
52
|
+
let depth = 1;
|
|
53
|
+
let k = j + 2;
|
|
54
|
+
while (k < n && depth > 0) {
|
|
55
|
+
const e = line.charCodeAt(k);
|
|
56
|
+
if (e === 123) depth++;
|
|
57
|
+
else if (e === 125) depth--;
|
|
58
|
+
if (depth > 0) k++;
|
|
59
|
+
}
|
|
60
|
+
parts.push(line.slice(j + 2, k));
|
|
61
|
+
j = k + 1;
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
j++;
|
|
65
|
+
}
|
|
66
|
+
out.push("\"\"");
|
|
67
|
+
for (const p of parts) out.push("(", ...tokens(p), ")");
|
|
68
|
+
i = j + 1;
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (is_id_start(c)) {
|
|
72
|
+
let j = i + 1;
|
|
73
|
+
while (j < n && is_id_part(line.charCodeAt(j))) j++;
|
|
74
|
+
out.push(line.slice(i, j));
|
|
75
|
+
i = j;
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
if (c >= 48 && c <= 57) {
|
|
79
|
+
let j = i + 1;
|
|
80
|
+
while (j < n && (is_id_part(line.charCodeAt(j)) || line.charCodeAt(j) === 46)) j++;
|
|
81
|
+
out.push("0");
|
|
82
|
+
i = j;
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
if (c === 46 && line.charCodeAt(i + 1) === 46 && line.charCodeAt(i + 2) === 46) {
|
|
86
|
+
out.push("...");
|
|
87
|
+
i += 3;
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
const two = line.slice(i, i + 2);
|
|
91
|
+
if (PUNCT2.has(two)) {
|
|
92
|
+
out.push(two);
|
|
93
|
+
i += 2;
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
out.push(line[i]);
|
|
97
|
+
i++;
|
|
98
|
+
}
|
|
99
|
+
return out;
|
|
100
|
+
}
|
|
101
|
+
/** The lines of a function from its first line to its closing brace (brace-balanced over tokens,
|
|
102
|
+
* so braces in strings and comments do not count); a braceless arrow is its one line. Capped. */
|
|
103
|
+
function function_body(source, path, start, max = 400) {
|
|
104
|
+
const got = source(path, start, start + max);
|
|
105
|
+
if (!got) return void 0;
|
|
106
|
+
let depth = 0;
|
|
107
|
+
let opened = false;
|
|
108
|
+
for (let i = 0; i < got.lines.length; i++) {
|
|
109
|
+
for (const t of tokens(got.lines[i])) if (t === "{") {
|
|
110
|
+
depth++;
|
|
111
|
+
opened = true;
|
|
112
|
+
} else if (t === "}") depth--;
|
|
113
|
+
if (opened && depth <= 0) return {
|
|
114
|
+
start: got.start,
|
|
115
|
+
lines: got.lines.slice(0, i + 1)
|
|
116
|
+
};
|
|
117
|
+
if (!opened && i === 0 && got.lines[0].includes("=>")) return {
|
|
118
|
+
start: got.start,
|
|
119
|
+
lines: got.lines.slice(0, 1)
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
return got;
|
|
123
|
+
}
|
|
124
|
+
/** the first lines of a file, where its imports and top-level wrappers sit */
|
|
125
|
+
const HEAD_LINES = 400;
|
|
126
|
+
/** A module's import aliases: `import { renderToString as render } from 'x'` → renderToString → [render]. */
|
|
127
|
+
function import_aliases(source, path) {
|
|
128
|
+
const out = /* @__PURE__ */ new Map();
|
|
129
|
+
const head = source(path, 1, HEAD_LINES);
|
|
130
|
+
if (!head) return out;
|
|
131
|
+
let inside = false;
|
|
132
|
+
for (const line of head.lines) {
|
|
133
|
+
const t = tokens(line);
|
|
134
|
+
for (let k = 0; k < t.length; k++) if (t[k] === "import") inside = true;
|
|
135
|
+
else if (t[k] === "from" || t[k] === ";") inside = false;
|
|
136
|
+
else if (inside && t[k] === "as" && t[k - 1] && t[k + 1] && t[k - 1] !== "*") push(out, t[k - 1], t[k + 1]);
|
|
137
|
+
}
|
|
138
|
+
return out;
|
|
139
|
+
}
|
|
140
|
+
/** The names a function also goes by in its module when it is WRAPPED:
|
|
141
|
+
* `const renderToString = instrument(render, 'ds.render')` → render → [renderToString].
|
|
142
|
+
* The shape is `const|let|var X = <call chain>(name` — the function is the wrapper's first argument. */
|
|
143
|
+
function wrapper_aliases(source, path) {
|
|
144
|
+
const out = /* @__PURE__ */ new Map();
|
|
145
|
+
const head = source(path, 1, HEAD_LINES);
|
|
146
|
+
if (!head) return out;
|
|
147
|
+
for (const line of head.lines) {
|
|
148
|
+
const t = tokens(line);
|
|
149
|
+
for (let k = 0; k + 3 < t.length; k++) {
|
|
150
|
+
if (t[k] !== "const" && t[k] !== "let" && t[k] !== "var" || t[k + 2] !== "=") continue;
|
|
151
|
+
let j = k + 3;
|
|
152
|
+
while (j < t.length && (t[j] === "." || t[j] !== "(" && t[j] !== ")" && is_id_start(t[j].charCodeAt(0)))) j++;
|
|
153
|
+
if (j === k + 3 || t[j] !== "(" || !t[j + 1] || !is_id_start(t[j + 1].charCodeAt(0))) continue;
|
|
154
|
+
if (t[j + 2] !== "," && t[j + 2] !== ")") continue;
|
|
155
|
+
push(out, t[j + 1], t[k + 1]);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return out;
|
|
159
|
+
}
|
|
160
|
+
/** how V8 names a compiled regular expression's code in a CPU profile: `RegExp: ^(\w+\s?)*$` */
|
|
161
|
+
const REGEXP_FRAME = "RegExp: ";
|
|
162
|
+
/**
|
|
163
|
+
* CAN THIS PATTERN BACKTRACK HARD? A repeated group that itself repeats something (`(\w+\s?)*`,
|
|
164
|
+
* `(a+)+`, `(?:x*y?)+`): the engine can split the same text many ways and tries them all before a
|
|
165
|
+
* match fails. (A repeated choice, `(a|aa)+`, can too, but `(foo|bar)+` is the usual one and
|
|
166
|
+
* harmless: not claimed.) A character scan, not a parse: escapes and classes are skipped over.
|
|
167
|
+
*/
|
|
168
|
+
function backtracks(source) {
|
|
169
|
+
if (!source) return false;
|
|
170
|
+
/** what follows an atom at `i`: `'many'` (`*`, `+`, `{n,}`), `'maybe'` (`?`, `{0,1}`-ish), or
|
|
171
|
+
* `'once'` (none, or a fixed `{n}`) */
|
|
172
|
+
const after = (i) => {
|
|
173
|
+
const c = source[i];
|
|
174
|
+
if (c === "*" || c === "+") return "many";
|
|
175
|
+
if (c === "?") return "maybe";
|
|
176
|
+
if (c === "{") {
|
|
177
|
+
const close = source.indexOf("}", i);
|
|
178
|
+
const comma = source.indexOf(",", i);
|
|
179
|
+
if (close !== -1 && comma !== -1 && comma < close) return "many";
|
|
180
|
+
}
|
|
181
|
+
return "once";
|
|
182
|
+
};
|
|
183
|
+
const stack = [{
|
|
184
|
+
repeats: false,
|
|
185
|
+
fixed: false,
|
|
186
|
+
choice: false
|
|
187
|
+
}];
|
|
188
|
+
const top = () => stack[stack.length - 1];
|
|
189
|
+
const atom = (q) => {
|
|
190
|
+
if (q === "many") top().repeats = true;
|
|
191
|
+
else if (q === "once") top().fixed = true;
|
|
192
|
+
};
|
|
193
|
+
for (let i = 0; i < source.length; i++) {
|
|
194
|
+
const c = source[i];
|
|
195
|
+
if (c === "\\") {
|
|
196
|
+
i++;
|
|
197
|
+
atom(after(i + 1));
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
if (c === "[") {
|
|
201
|
+
for (i++; i < source.length && source[i] !== "]"; i++) if (source[i] === "\\") i++;
|
|
202
|
+
atom(after(i + 1));
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
if (c === "(") {
|
|
206
|
+
stack.push({
|
|
207
|
+
repeats: false,
|
|
208
|
+
fixed: false,
|
|
209
|
+
choice: false
|
|
210
|
+
});
|
|
211
|
+
if (source[i + 1] === "?") {
|
|
212
|
+
i++;
|
|
213
|
+
if (source[i + 1] === "<" && source[i + 2] !== "=" && source[i + 2] !== "!") {
|
|
214
|
+
const end = source.indexOf(">", i);
|
|
215
|
+
if (end !== -1) i = end;
|
|
216
|
+
} else i++;
|
|
217
|
+
}
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
if (c === ")") {
|
|
221
|
+
if (stack.length < 2) continue;
|
|
222
|
+
const g = stack.pop();
|
|
223
|
+
const q = after(i + 1);
|
|
224
|
+
if (q === "many" && g.repeats && !g.fixed && !g.choice) return true;
|
|
225
|
+
if (q === "many" || g.repeats) top().repeats = true;
|
|
226
|
+
if (q === "once" && g.fixed) top().fixed = true;
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
if (c === "|") {
|
|
230
|
+
top().choice = true;
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
if (c === "{") {
|
|
234
|
+
const close = source.indexOf("}", i);
|
|
235
|
+
if (close !== -1) i = close;
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
if (c === "^" || c === "$" || c === "*" || c === "+" || c === "?" || c === "}") continue;
|
|
239
|
+
atom(after(i + 1));
|
|
240
|
+
}
|
|
241
|
+
return false;
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* THE REGEX LITERALS WRITTEN ON A LINE (their source, without the slashes and flags): a `/` where
|
|
245
|
+
* an expression starts (after `(`, `,`, `=`, `:`, `[`, `!`, `&`, `|`, `?`, `{`, `;`, `return`, or
|
|
246
|
+
* at the start) opens one. Strings, template text and comments are skipped; a division is not a
|
|
247
|
+
* literal (it follows a value).
|
|
248
|
+
*/
|
|
249
|
+
function regex_literals(code) {
|
|
250
|
+
const out = [];
|
|
251
|
+
let prev = "";
|
|
252
|
+
let word = "";
|
|
253
|
+
for (let i = 0; i < code.length; i++) {
|
|
254
|
+
const c = code[i];
|
|
255
|
+
if (c === "\"" || c === "'" || c === "`") {
|
|
256
|
+
for (i++; i < code.length && code[i] !== c; i++) if (code[i] === "\\") i++;
|
|
257
|
+
prev = c;
|
|
258
|
+
word = "";
|
|
259
|
+
continue;
|
|
260
|
+
}
|
|
261
|
+
if (c === "/" && code[i + 1] === "/") break;
|
|
262
|
+
if (c === "/" && code[i + 1] === "*") {
|
|
263
|
+
const end = code.indexOf("*/", i + 2);
|
|
264
|
+
if (end === -1) break;
|
|
265
|
+
i = end + 1;
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
if (c === "/") {
|
|
269
|
+
if (prev === "" || "(,=:[!&|?{};".includes(prev) || word === "return") {
|
|
270
|
+
let j = i + 1;
|
|
271
|
+
let in_class = false;
|
|
272
|
+
for (; j < code.length; j++) {
|
|
273
|
+
const d = code[j];
|
|
274
|
+
if (d === "\\") {
|
|
275
|
+
j++;
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
if (d === "[") in_class = true;
|
|
279
|
+
else if (d === "]") in_class = false;
|
|
280
|
+
else if (d === "/" && !in_class) break;
|
|
281
|
+
}
|
|
282
|
+
if (j < code.length && j > i + 1) {
|
|
283
|
+
out.push(code.slice(i + 1, j));
|
|
284
|
+
i = j;
|
|
285
|
+
while (i + 1 < code.length && ID_CHAR(code[i + 1])) i++;
|
|
286
|
+
prev = ")";
|
|
287
|
+
word = "";
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
if (c === " " || c === " ") continue;
|
|
293
|
+
word = ID_CHAR(c) ? ID_CHAR(prev) ? word + c : c : "";
|
|
294
|
+
prev = c;
|
|
295
|
+
}
|
|
296
|
+
return out;
|
|
297
|
+
}
|
|
298
|
+
const ID_CHAR = (c) => !!c && (c >= "a" && c <= "z" || c >= "A" && c <= "Z" || c >= "0" && c <= "9" || c === "_" || c === "$");
|
|
299
|
+
/** does this line call `name(` (plain or as a method), outside strings and comments? */
|
|
300
|
+
function calls_name(line, names) {
|
|
301
|
+
const t = tokens(line);
|
|
302
|
+
for (let k = 0; k + 1 < t.length; k++) if (t[k + 1] === "(" && names.includes(t[k]) && t[k - 1] !== "function") return true;
|
|
303
|
+
return false;
|
|
304
|
+
}
|
|
305
|
+
/** The functions a hooks file's `handle` is made of, when they live in OTHER app modules:
|
|
306
|
+
* `export const handle = sequence(ds_ssr, auth)` with `import { ds_ssr } from '$lib/ds-ssr'` →
|
|
307
|
+
* ds_ssr → `ds-ssr`. The value is the module's file stem, so a CPU frame counts as that handle
|
|
308
|
+
* only when it is named so AND sits in that file. A handle written in the hooks file itself is
|
|
309
|
+
* not listed (its frames are in the hooks file already), and neither is one from a package (a
|
|
310
|
+
* library's frames say what they are) or one made by a call (`auth()`: what it returns has its
|
|
311
|
+
* own name). */
|
|
312
|
+
function handle_parts(source, path) {
|
|
313
|
+
const out = /* @__PURE__ */ new Map();
|
|
314
|
+
const text = source(path, 1, 2e3);
|
|
315
|
+
if (!text) return out;
|
|
316
|
+
/** local name → the name it was exported under and the module's file stem */
|
|
317
|
+
const imported = /* @__PURE__ */ new Map();
|
|
318
|
+
let pending = [];
|
|
319
|
+
let inside = false;
|
|
320
|
+
const all = [];
|
|
321
|
+
for (const line of text.lines) {
|
|
322
|
+
const t = tokens(line);
|
|
323
|
+
for (let k = 0; k < t.length; k++) {
|
|
324
|
+
const w = t[k];
|
|
325
|
+
if (w === "import" && (k === 0 || t[k - 1] === ";")) {
|
|
326
|
+
inside = true;
|
|
327
|
+
pending = [];
|
|
328
|
+
} else if (inside && w === "from") {
|
|
329
|
+
const stem = app_module_stem(spec_after_from(line));
|
|
330
|
+
if (stem) for (const [name, local] of pending) imported.set(local, {
|
|
331
|
+
name,
|
|
332
|
+
stem
|
|
333
|
+
});
|
|
334
|
+
inside = false;
|
|
335
|
+
} else if (inside && w === "type") k++;
|
|
336
|
+
else if (inside && is_id_start(w.charCodeAt(0)) && w !== "as") {
|
|
337
|
+
if (t[k + 1] === "as" && t[k + 2]) {
|
|
338
|
+
pending.push([w, t[k + 2]]);
|
|
339
|
+
k += 2;
|
|
340
|
+
} else pending.push([w, w]);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
for (const w of t) all.push(w);
|
|
344
|
+
}
|
|
345
|
+
for (let k = 0; k + 3 < all.length; k++) {
|
|
346
|
+
if (all[k] !== "export" || all[k + 1] !== "const" && all[k + 1] !== "let" || all[k + 2] !== "handle") continue;
|
|
347
|
+
let j = k + 3;
|
|
348
|
+
while (j < all.length && all[j] !== "=") j++;
|
|
349
|
+
j++;
|
|
350
|
+
const take = (name) => {
|
|
351
|
+
const hit = imported.get(name);
|
|
352
|
+
if (hit) out.set(hit.name, hit.stem);
|
|
353
|
+
};
|
|
354
|
+
if (all[j + 1] !== "(") {
|
|
355
|
+
take(all[j]);
|
|
356
|
+
break;
|
|
357
|
+
}
|
|
358
|
+
let depth = 0;
|
|
359
|
+
for (let i = j + 1; i < all.length; i++) {
|
|
360
|
+
const w = all[i];
|
|
361
|
+
if (w === "(" || w === "[" || w === "{") depth++;
|
|
362
|
+
else if (w === ")" || w === "]" || w === "}") {
|
|
363
|
+
if (--depth === 0) break;
|
|
364
|
+
} else if (depth === 1 && (all[i - 1] === "(" || all[i - 1] === ",") && (all[i + 1] === "," || all[i + 1] === ")")) take(w);
|
|
365
|
+
}
|
|
366
|
+
break;
|
|
367
|
+
}
|
|
368
|
+
return out;
|
|
369
|
+
}
|
|
370
|
+
/** the quoted module specifier after the last `from` on a line (`''` when there is none) */
|
|
371
|
+
function spec_after_from(line) {
|
|
372
|
+
const at = line.lastIndexOf("from");
|
|
373
|
+
if (at === -1) return "";
|
|
374
|
+
let i = at + 4;
|
|
375
|
+
while (i < line.length && line[i] !== "'" && line[i] !== "\"") i++;
|
|
376
|
+
const end = line.indexOf(line[i], i + 1);
|
|
377
|
+
return end === -1 ? "" : line.slice(i + 1, end);
|
|
378
|
+
}
|
|
379
|
+
/** an app module's file stem (`$lib/hell/ds-ssr` → `ds-ssr`, `./auth.js` → `auth`); undefined for
|
|
380
|
+
* a package (`@sveltejs/kit/hooks`, `ogygia/server`) */
|
|
381
|
+
function app_module_stem(spec) {
|
|
382
|
+
const c = spec[0];
|
|
383
|
+
if (c !== "." && c !== "/" && c !== "$" && c !== "~" && c !== "#") return void 0;
|
|
384
|
+
const leaf = spec.slice(spec.lastIndexOf("/") + 1);
|
|
385
|
+
const dot = leaf.indexOf(".");
|
|
386
|
+
const stem = dot === -1 ? leaf : leaf.slice(0, dot);
|
|
387
|
+
return stem && stem !== "index" ? stem : void 0;
|
|
388
|
+
}
|
|
389
|
+
function push(m, k, v) {
|
|
390
|
+
const list = m.get(k);
|
|
391
|
+
if (!list) m.set(k, [v]);
|
|
392
|
+
else if (!list.includes(v)) list.push(v);
|
|
393
|
+
}
|
|
394
|
+
/** a word that is not a variable: the language's own, and the ones a statement reads by */
|
|
395
|
+
const NOT_A_NAME = /* @__PURE__ */ new Set([
|
|
396
|
+
"await",
|
|
397
|
+
"async",
|
|
398
|
+
"return",
|
|
399
|
+
"const",
|
|
400
|
+
"let",
|
|
401
|
+
"var",
|
|
402
|
+
"new",
|
|
403
|
+
"typeof",
|
|
404
|
+
"instanceof",
|
|
405
|
+
"in",
|
|
406
|
+
"of",
|
|
407
|
+
"if",
|
|
408
|
+
"else",
|
|
409
|
+
"for",
|
|
410
|
+
"while",
|
|
411
|
+
"do",
|
|
412
|
+
"function",
|
|
413
|
+
"true",
|
|
414
|
+
"false",
|
|
415
|
+
"null",
|
|
416
|
+
"undefined",
|
|
417
|
+
"this",
|
|
418
|
+
"as",
|
|
419
|
+
"satisfies",
|
|
420
|
+
"void",
|
|
421
|
+
"delete",
|
|
422
|
+
"yield",
|
|
423
|
+
"throw",
|
|
424
|
+
"try",
|
|
425
|
+
"catch",
|
|
426
|
+
"finally",
|
|
427
|
+
"switch",
|
|
428
|
+
"case",
|
|
429
|
+
"break",
|
|
430
|
+
"continue",
|
|
431
|
+
"default",
|
|
432
|
+
"class",
|
|
433
|
+
"extends",
|
|
434
|
+
"super",
|
|
435
|
+
"import",
|
|
436
|
+
"export"
|
|
437
|
+
]);
|
|
438
|
+
/** a method that fills the value it is called on: `list.push(x)` writes `list` */
|
|
439
|
+
const FILLS = /* @__PURE__ */ new Set([
|
|
440
|
+
"push",
|
|
441
|
+
"unshift",
|
|
442
|
+
"set",
|
|
443
|
+
"add",
|
|
444
|
+
"splice",
|
|
445
|
+
"assign"
|
|
446
|
+
]);
|
|
447
|
+
/**
|
|
448
|
+
* WHERE EACH RETURNED KEY COMES FROM: the lines of a `load` whose values end up in each key of its
|
|
449
|
+
* `return { … }` — a key's expression, the variables it reads, the lines that declare or fill those
|
|
450
|
+
* (`const live = await fetch(…)`, `stock.push(await stockOf(…))`), and so on back through what they
|
|
451
|
+
* read. 1-based line numbers of `source`. A wait made on one of a key's lines is a wait for that
|
|
452
|
+
* key. Tokens only (no regex); null when there is no `load` or its return is not an object literal.
|
|
453
|
+
*/
|
|
454
|
+
/**
|
|
455
|
+
* WHERE A FILE'S `load` IS: its `export` line, and the lines its block body opens and closes on
|
|
456
|
+
* (0-based). `export async function load(…): Promise<{ a: string }> {` — the return type's braces
|
|
457
|
+
* are skipped; `export const load = async (…) => {` — the block after its arrow (a `satisfies`
|
|
458
|
+
* wrapper around it too). An expression body (`=> ({ … })`) has no block: null, never the next
|
|
459
|
+
* function in the file.
|
|
460
|
+
*/
|
|
461
|
+
function load_span(T) {
|
|
462
|
+
let head = -1;
|
|
463
|
+
let is_fn = false;
|
|
464
|
+
for (let i = 0; i < T.length && head < 0; i++) {
|
|
465
|
+
const t = T[i];
|
|
466
|
+
if (t[0] !== "export") continue;
|
|
467
|
+
for (let k = 1; k + 1 < t.length; k++) if (t[k + 1] === "load" && (t[k] === "const" || t[k] === "function")) {
|
|
468
|
+
head = i;
|
|
469
|
+
is_fn = t[k] === "function";
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
if (head < 0) return null;
|
|
473
|
+
const flat = [];
|
|
474
|
+
for (let i = head; i < T.length; i++) for (const t of T[i]) flat.push({
|
|
475
|
+
t,
|
|
476
|
+
line: i
|
|
477
|
+
});
|
|
478
|
+
let k = flat.findIndex((x) => x.t === "load");
|
|
479
|
+
let open = -1;
|
|
480
|
+
if (is_fn) {
|
|
481
|
+
while (k < flat.length && flat[k].t !== "(") k++;
|
|
482
|
+
let paren = 0;
|
|
483
|
+
for (; k < flat.length; k++) if (flat[k].t === "(") paren++;
|
|
484
|
+
else if (flat[k].t === ")" && --paren === 0) break;
|
|
485
|
+
let angle = 0;
|
|
486
|
+
for (k++; k < flat.length && open < 0; k++) {
|
|
487
|
+
const x = flat[k].t;
|
|
488
|
+
const prev = flat[k - 1].t;
|
|
489
|
+
if (x === "<") angle++;
|
|
490
|
+
else if (x === ">") angle--;
|
|
491
|
+
else if (x === "{" && angle === 0) {
|
|
492
|
+
if (prev === ":" || prev === "|" || prev === "&" || prev === "(") {
|
|
493
|
+
let d = 0;
|
|
494
|
+
for (; k < flat.length; k++) if (flat[k].t === "{") d++;
|
|
495
|
+
else if (flat[k].t === "}" && --d === 0) break;
|
|
496
|
+
} else open = k;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
} else {
|
|
500
|
+
while (k < flat.length && flat[k].t !== "=>") k++;
|
|
501
|
+
if (k + 1 < flat.length && flat[k + 1].t === "{") open = k + 1;
|
|
502
|
+
}
|
|
503
|
+
if (open < 0) return null;
|
|
504
|
+
let d = 0;
|
|
505
|
+
for (let j = open; j < flat.length; j++) if (flat[j].t === "{") d++;
|
|
506
|
+
else if (flat[j].t === "}" && --d === 0) return {
|
|
507
|
+
head,
|
|
508
|
+
body0: flat[open].line,
|
|
509
|
+
body1: flat[j].line
|
|
510
|
+
};
|
|
511
|
+
return null;
|
|
512
|
+
}
|
|
513
|
+
function key_sources(source) {
|
|
514
|
+
const T = source.split("\n").map((l) => tokens(l));
|
|
515
|
+
const span = load_span(T);
|
|
516
|
+
if (!span) return null;
|
|
517
|
+
const { body0, body1 } = span;
|
|
518
|
+
const names = /* @__PURE__ */ new Map();
|
|
519
|
+
const name_of = (n) => names.get(n) ?? names.set(n, {
|
|
520
|
+
lines: /* @__PURE__ */ new Set(),
|
|
521
|
+
deps: /* @__PURE__ */ new Set(),
|
|
522
|
+
decl: []
|
|
523
|
+
}).get(n);
|
|
524
|
+
/** the variables a stretch of tokens reads — not a property (`x.p`), and not a callback's own
|
|
525
|
+
* parameter (`(p) => …`, `p => …`: a local, whatever else is called `p`) */
|
|
526
|
+
const ids = (list) => {
|
|
527
|
+
const params = /* @__PURE__ */ new Set();
|
|
528
|
+
for (let m = 0; m < list.length; m++) {
|
|
529
|
+
if (list[m] !== "=>") continue;
|
|
530
|
+
if (list[m - 1] !== ")") {
|
|
531
|
+
if (list[m - 1]) params.add(list[m - 1]);
|
|
532
|
+
continue;
|
|
533
|
+
}
|
|
534
|
+
let d = 0;
|
|
535
|
+
for (let j = m - 1; j >= 0; j--) if (list[j] === ")") d++;
|
|
536
|
+
else if (list[j] === "(" && --d === 0) break;
|
|
537
|
+
else if (d >= 1 && is_id_start(list[j].charCodeAt(0)) && list[j - 1] !== ":") params.add(list[j]);
|
|
538
|
+
}
|
|
539
|
+
return list.filter((t, k) => is_id_start(t.charCodeAt(0)) && !NOT_A_NAME.has(t) && !params.has(t) && list[k - 1] !== "." && list[k - 1] !== "?.");
|
|
540
|
+
};
|
|
541
|
+
for (let i = body0; i <= body1; i++) {
|
|
542
|
+
const t = T[i];
|
|
543
|
+
for (let k = 0; k < t.length; k++) {
|
|
544
|
+
if (t[k] !== "const" && t[k] !== "let" && t[k] !== "var") continue;
|
|
545
|
+
const declared = [];
|
|
546
|
+
let j = k + 1;
|
|
547
|
+
let depth = 0;
|
|
548
|
+
let typed = false;
|
|
549
|
+
for (; j < t.length; j++) {
|
|
550
|
+
const x = t[j];
|
|
551
|
+
if (depth === 0 && (x === "=" || x === "of" || x === "in")) break;
|
|
552
|
+
if (x === "{" || x === "[" || x === "(" || x === "<") depth++;
|
|
553
|
+
else if (x === "}" || x === "]" || x === ")" || x === ">") depth--;
|
|
554
|
+
else if (x === ":" && depth === 0) typed = true;
|
|
555
|
+
else if (!typed && is_id_start(x.charCodeAt(0)) && !NOT_A_NAME.has(x) && (depth === 0 || t[j + 1] !== ":")) declared.push(x);
|
|
556
|
+
}
|
|
557
|
+
if (!declared.length) continue;
|
|
558
|
+
if (j >= t.length) {
|
|
559
|
+
for (const d of declared) {
|
|
560
|
+
const n = name_of(d);
|
|
561
|
+
n.decl.push(/* @__PURE__ */ new Set([i + 1]));
|
|
562
|
+
n.lines.add(i + 1);
|
|
563
|
+
}
|
|
564
|
+
break;
|
|
565
|
+
}
|
|
566
|
+
const rhs = t.slice(j + 1);
|
|
567
|
+
const at = /* @__PURE__ */ new Set([i + 1]);
|
|
568
|
+
let open = 0;
|
|
569
|
+
for (const x of rhs) open += x === "(" || x === "[" || x === "{" ? 1 : x === ")" || x === "]" || x === "}" ? -1 : 0;
|
|
570
|
+
for (let m = i + 1; open > 0 && m <= body1; m++) {
|
|
571
|
+
at.add(m + 1);
|
|
572
|
+
for (const x of T[m]) {
|
|
573
|
+
rhs.push(x);
|
|
574
|
+
open += x === "(" || x === "[" || x === "{" ? 1 : x === ")" || x === "]" || x === "}" ? -1 : 0;
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
const deps = ids(rhs);
|
|
578
|
+
for (const d of declared) {
|
|
579
|
+
const n = name_of(d);
|
|
580
|
+
n.decl.push(at);
|
|
581
|
+
for (const l of at) n.lines.add(l);
|
|
582
|
+
for (const x of deps) if (x !== d) n.deps.add(x);
|
|
583
|
+
}
|
|
584
|
+
break;
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
for (let i = body0; i <= body1; i++) {
|
|
588
|
+
const t = T[i];
|
|
589
|
+
for (let k = 0; k < t.length; k++) {
|
|
590
|
+
const n = names.get(t[k]);
|
|
591
|
+
if (!n || t[k - 1] === "." || t[k - 1] === "?.") continue;
|
|
592
|
+
const fills = t[k + 1] === "." && FILLS.has(t[k + 2]) && t[k + 3] === "(";
|
|
593
|
+
let after = k + 1;
|
|
594
|
+
if (t[after] === "[") {
|
|
595
|
+
let d = 0;
|
|
596
|
+
for (; after < t.length; after++) if (t[after] === "[") d++;
|
|
597
|
+
else if (t[after] === "]" && --d === 0) break;
|
|
598
|
+
after++;
|
|
599
|
+
} else if (t[after] === "." && t[after + 2] === "=") after += 2;
|
|
600
|
+
const assigns = (t[after] === "=" || t[after] === "+=") && (k === 0 || t[k - 1] === ";" || t[k - 1] === "{" || t[k - 1] === ")" || t[k - 1] === "else");
|
|
601
|
+
if (!fills && !assigns) continue;
|
|
602
|
+
n.lines.add(i + 1);
|
|
603
|
+
for (const x of ids(t)) if (x !== t[k]) n.deps.add(x);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
for (const n of names.values()) for (const d of Array.from(n.deps)) if (names.get(d)?.decl.some((set) => [...set].every((l) => n.lines.has(l)))) n.deps.delete(d);
|
|
607
|
+
let ret = -1;
|
|
608
|
+
{
|
|
609
|
+
let depth = 0;
|
|
610
|
+
for (let i = body0; i <= body1; i++) {
|
|
611
|
+
const t = T[i];
|
|
612
|
+
for (let k = 0; k < t.length; k++) if (t[k] === "{") depth++;
|
|
613
|
+
else if (t[k] === "}") depth--;
|
|
614
|
+
else if (t[k] === "return" && depth === 1 && (t[k + 1] === "{" || t[k + 1] === "(" && t[k + 2] === "{")) ret = i;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
if (ret < 0) return null;
|
|
618
|
+
const flat = [];
|
|
619
|
+
for (let i = ret; i <= body1; i++) for (const t of T[i]) flat.push({
|
|
620
|
+
t,
|
|
621
|
+
line: i + 1
|
|
622
|
+
});
|
|
623
|
+
let s = flat.findIndex((x) => x.t === "return");
|
|
624
|
+
while (s < flat.length && flat[s].t !== "{") s++;
|
|
625
|
+
const keys = /* @__PURE__ */ new Map();
|
|
626
|
+
{
|
|
627
|
+
let depth = 0;
|
|
628
|
+
let key = null;
|
|
629
|
+
let expect_key = true;
|
|
630
|
+
for (let i = s; i < flat.length; i++) {
|
|
631
|
+
const x = flat[i].t;
|
|
632
|
+
if (x === "{" || x === "[" || x === "(") {
|
|
633
|
+
depth++;
|
|
634
|
+
if (depth === 1) continue;
|
|
635
|
+
} else if (x === "}" || x === "]" || x === ")") {
|
|
636
|
+
depth--;
|
|
637
|
+
if (depth === 0) break;
|
|
638
|
+
}
|
|
639
|
+
if (depth === 1 && x === ",") {
|
|
640
|
+
key = null;
|
|
641
|
+
expect_key = true;
|
|
642
|
+
continue;
|
|
643
|
+
}
|
|
644
|
+
if (depth === 1 && expect_key) {
|
|
645
|
+
expect_key = false;
|
|
646
|
+
if (x === "...") continue;
|
|
647
|
+
if (!is_id_start(x.charCodeAt(0))) continue;
|
|
648
|
+
key = x;
|
|
649
|
+
const entry = {
|
|
650
|
+
reads: [],
|
|
651
|
+
lines: /* @__PURE__ */ new Set([flat[i].line]),
|
|
652
|
+
toks: []
|
|
653
|
+
};
|
|
654
|
+
keys.set(x, entry);
|
|
655
|
+
const next = flat[i + 1]?.t;
|
|
656
|
+
if (next === "," || next === "}") entry.reads.push(x);
|
|
657
|
+
continue;
|
|
658
|
+
}
|
|
659
|
+
if (key && x !== ":") {
|
|
660
|
+
const k = keys.get(key);
|
|
661
|
+
k.lines.add(flat[i].line);
|
|
662
|
+
k.toks.push(x);
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
if (!keys.size) return null;
|
|
667
|
+
for (const k of keys.values()) k.reads.push(...ids(k.toks));
|
|
668
|
+
const helpers = /* @__PURE__ */ new Map();
|
|
669
|
+
for (let i = 0; i < T.length; i++) {
|
|
670
|
+
if (i >= body0 && i <= body1) continue;
|
|
671
|
+
const t = T[i];
|
|
672
|
+
const f = t.indexOf("function");
|
|
673
|
+
const c = t.indexOf("const");
|
|
674
|
+
const name = f !== -1 && is_id_start((t[f + 1] ?? "").charCodeAt(0)) ? t[f + 1] : c !== -1 && t[c + 2] === "=" && t.includes("=>") ? t[c + 1] : void 0;
|
|
675
|
+
if (!name || name === "load") continue;
|
|
676
|
+
let depth = 0;
|
|
677
|
+
let opened = false;
|
|
678
|
+
const span = [];
|
|
679
|
+
for (let j = i; j < T.length; j++) {
|
|
680
|
+
span.push(j + 1);
|
|
681
|
+
for (const x of T[j]) if (x === "{") {
|
|
682
|
+
depth++;
|
|
683
|
+
opened = true;
|
|
684
|
+
} else if (x === "}") depth--;
|
|
685
|
+
if (opened && depth <= 0 || !opened && j === i) break;
|
|
686
|
+
}
|
|
687
|
+
helpers.set(name, span);
|
|
688
|
+
}
|
|
689
|
+
const calls_on = (line) => T[line - 1]?.filter((x, k, t) => helpers.has(x) && t[k + 1] === "(") ?? [];
|
|
690
|
+
const out = /* @__PURE__ */ new Map();
|
|
691
|
+
for (const [key, k] of keys) {
|
|
692
|
+
const lines_of = new Set(k.lines);
|
|
693
|
+
const seen = /* @__PURE__ */ new Set();
|
|
694
|
+
const stack = [...k.reads];
|
|
695
|
+
while (stack.length) {
|
|
696
|
+
const n = stack.pop();
|
|
697
|
+
if (seen.has(n)) continue;
|
|
698
|
+
seen.add(n);
|
|
699
|
+
const d = names.get(n);
|
|
700
|
+
if (!d) continue;
|
|
701
|
+
for (const l of d.lines) lines_of.add(l);
|
|
702
|
+
for (const x of d.deps) stack.push(x);
|
|
703
|
+
}
|
|
704
|
+
const todo = [...lines_of];
|
|
705
|
+
const took = /* @__PURE__ */ new Set();
|
|
706
|
+
while (todo.length) for (const h of calls_on(todo.pop())) {
|
|
707
|
+
if (took.has(h)) continue;
|
|
708
|
+
took.add(h);
|
|
709
|
+
for (const l of helpers.get(h)) if (!lines_of.has(l)) {
|
|
710
|
+
lines_of.add(l);
|
|
711
|
+
todo.push(l);
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
out.set(key, [...lines_of].sort((a, b) => a - b));
|
|
715
|
+
}
|
|
716
|
+
return out;
|
|
717
|
+
}
|
|
718
|
+
/** One line's reads and writes: the names it declares or assigns (`const a =`, `a =`, `a += `,
|
|
719
|
+
* `a[k] =`), the receivers it fills (`list.push(…)`), and the variables it reads (not a property,
|
|
720
|
+
* not a callback's own parameter, not a name it only writes). `declared`: written by a
|
|
721
|
+
* declaration (fresh each time the line runs: never carried from one loop turn to the next). */
|
|
722
|
+
function line_rw(line) {
|
|
723
|
+
const t = tokens(line);
|
|
724
|
+
const writes = /* @__PURE__ */ new Set();
|
|
725
|
+
const declared = /* @__PURE__ */ new Set();
|
|
726
|
+
/** `x += …` targets: added to, in any order */
|
|
727
|
+
const accum = /* @__PURE__ */ new Set();
|
|
728
|
+
const params = /* @__PURE__ */ new Set();
|
|
729
|
+
for (let m = 0; m < t.length; m++) {
|
|
730
|
+
if (t[m] !== "=>") continue;
|
|
731
|
+
if (t[m - 1] !== ")") {
|
|
732
|
+
if (t[m - 1]) params.add(t[m - 1]);
|
|
733
|
+
continue;
|
|
734
|
+
}
|
|
735
|
+
let d = 0;
|
|
736
|
+
for (let j = m - 1; j >= 0; j--) if (t[j] === ")") d++;
|
|
737
|
+
else if (t[j] === "(" && --d === 0) break;
|
|
738
|
+
else if (d >= 1 && is_id_start(t[j].charCodeAt(0)) && t[j - 1] !== ":") params.add(t[j]);
|
|
739
|
+
}
|
|
740
|
+
/** token indexes that are written, not read */
|
|
741
|
+
const skip = /* @__PURE__ */ new Set();
|
|
742
|
+
for (let k = 0; k < t.length; k++) {
|
|
743
|
+
if (t[k] === "const" || t[k] === "let" || t[k] === "var") {
|
|
744
|
+
let depth = 0;
|
|
745
|
+
for (let j = k + 1; j < t.length; j++) {
|
|
746
|
+
const x = t[j];
|
|
747
|
+
if (depth === 0 && (x === "=" || x === "of" || x === "in" || x === ":")) break;
|
|
748
|
+
if (x === "{" || x === "[") depth++;
|
|
749
|
+
else if (x === "}" || x === "]") depth--;
|
|
750
|
+
else if (is_id_start(x.charCodeAt(0)) && !NOT_A_NAME.has(x)) {
|
|
751
|
+
skip.add(j);
|
|
752
|
+
if (depth === 0 || t[j + 1] !== ":") {
|
|
753
|
+
declared.add(x);
|
|
754
|
+
writes.add(x);
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
continue;
|
|
759
|
+
}
|
|
760
|
+
if (!is_id_start(t[k].charCodeAt(0)) || NOT_A_NAME.has(t[k]) || t[k - 1] === "." || t[k - 1] === "?.") continue;
|
|
761
|
+
if (t[k + 1] === "." && FILLS.has(t[k + 2]) && t[k + 3] === "(") {
|
|
762
|
+
writes.add(t[k]);
|
|
763
|
+
skip.add(k);
|
|
764
|
+
continue;
|
|
765
|
+
}
|
|
766
|
+
let after = k + 1;
|
|
767
|
+
if (t[after] === "[") {
|
|
768
|
+
let d = 0;
|
|
769
|
+
for (; after < t.length; after++) if (t[after] === "[") d++;
|
|
770
|
+
else if (t[after] === "]" && --d === 0) break;
|
|
771
|
+
after++;
|
|
772
|
+
} else if (t[after] === "." && t[after + 2] === "=") after += 2;
|
|
773
|
+
if (t[after] === "=" || t[after] === "+=") {
|
|
774
|
+
writes.add(t[k]);
|
|
775
|
+
if (t[after] === "=") skip.add(k);
|
|
776
|
+
else accum.add(t[k]);
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
const reads = /* @__PURE__ */ new Set();
|
|
780
|
+
for (let k = 0; k < t.length; k++) {
|
|
781
|
+
const x = t[k];
|
|
782
|
+
if (skip.has(k) || !is_id_start(x.charCodeAt(0)) || NOT_A_NAME.has(x) || params.has(x) || t[k - 1] === "." || t[k - 1] === "?.") continue;
|
|
783
|
+
if (t[k + 1] === ":" && (t[k - 1] === "{" || t[k - 1] === ",")) continue;
|
|
784
|
+
reads.add(x);
|
|
785
|
+
}
|
|
786
|
+
return {
|
|
787
|
+
reads: [...reads],
|
|
788
|
+
writes: [...writes],
|
|
789
|
+
declared: [...declared],
|
|
790
|
+
accum: [...accum]
|
|
791
|
+
};
|
|
792
|
+
}
|
|
793
|
+
/**
|
|
794
|
+
* DOES THE LATER CALL NEED THE EARLIER ONE'S ANSWER? Two lines of one file: true when `to` reads a
|
|
795
|
+
* name `from` wrote, directly or through the lines between (`const a = await x()` → `const id =
|
|
796
|
+
* a.id` → `await y(id)`). One line (a loop making the calls): true when a turn feeds the next — a
|
|
797
|
+
* name assigned (not declared) on it or the few lines after it, that it reads (`cursor = (await
|
|
798
|
+
* get(cursor)).next`). Then the calls cannot run together, and "waits in a row" is not a fix.
|
|
799
|
+
*/
|
|
800
|
+
function needs_previous(source, path, from, to) {
|
|
801
|
+
if (from === to) {
|
|
802
|
+
const got = source(path, from, from + 3);
|
|
803
|
+
if (!got) return false;
|
|
804
|
+
const here = line_rw(got.lines[0]);
|
|
805
|
+
const carried = /* @__PURE__ */ new Set();
|
|
806
|
+
for (const l of got.lines) {
|
|
807
|
+
const rw = line_rw(l);
|
|
808
|
+
for (const w of rw.writes) if (!rw.declared.includes(w)) carried.add(w);
|
|
809
|
+
}
|
|
810
|
+
return here.reads.some((r) => carried.has(r) && !here.accum.includes(r));
|
|
811
|
+
}
|
|
812
|
+
if (to < from || to - from > 200) return false;
|
|
813
|
+
const got = source(path, from, to);
|
|
814
|
+
if (!got || got.lines.length < 2) return false;
|
|
815
|
+
const produced = new Set(line_rw(got.lines[0]).writes);
|
|
816
|
+
for (let i = 1; i < got.lines.length; i++) {
|
|
817
|
+
const rw = line_rw(got.lines[i]);
|
|
818
|
+
const hit = rw.reads.some((r) => produced.has(r));
|
|
819
|
+
if (i === got.lines.length - 1) return hit;
|
|
820
|
+
if (hit) for (const w of rw.writes) produced.add(w);
|
|
821
|
+
}
|
|
822
|
+
return false;
|
|
823
|
+
}
|
|
824
|
+
/**
|
|
825
|
+
* WHAT THE PAGE WAITED ON THE LAYOUT FOR: in a load that does `await parent()`, the line doing it,
|
|
826
|
+
* the names it sets (`const { session } = await parent()` → session), and the first line after it
|
|
827
|
+
* that reads one of them (null: none does). The `await`s in between waited on the layout's loads
|
|
828
|
+
* for data they never used. 1-based lines; null when there is no `await parent()`.
|
|
829
|
+
*/
|
|
830
|
+
function parent_use(source) {
|
|
831
|
+
const lines = source.split("\n");
|
|
832
|
+
let at = -1;
|
|
833
|
+
for (let i = 0; i < lines.length && at < 0; i++) {
|
|
834
|
+
const t = tokens(lines[i]);
|
|
835
|
+
for (let k = 0; k + 2 < t.length; k++) if (t[k] === "await" && t[k + 1] === "parent" && t[k + 2] === "(") at = i;
|
|
836
|
+
}
|
|
837
|
+
if (at < 0) return null;
|
|
838
|
+
const names = line_rw(lines[at]).writes;
|
|
839
|
+
if (!names.length) return null;
|
|
840
|
+
const set = new Set(names);
|
|
841
|
+
let awaits = 0;
|
|
842
|
+
let depth = 0;
|
|
843
|
+
for (let i = at + 1; i < lines.length; i++) {
|
|
844
|
+
const t = tokens(lines[i]);
|
|
845
|
+
if (line_rw(lines[i]).reads.some((r) => set.has(r))) return {
|
|
846
|
+
line: at + 1,
|
|
847
|
+
names,
|
|
848
|
+
first_use: i + 1,
|
|
849
|
+
awaits_before_use: awaits
|
|
850
|
+
};
|
|
851
|
+
if (t.includes("await")) awaits++;
|
|
852
|
+
for (const x of t) depth += x === "{" ? 1 : x === "}" ? -1 : 0;
|
|
853
|
+
if (depth < 0) break;
|
|
854
|
+
}
|
|
855
|
+
return {
|
|
856
|
+
line: at + 1,
|
|
857
|
+
names,
|
|
858
|
+
first_use: null,
|
|
859
|
+
awaits_before_use: awaits
|
|
860
|
+
};
|
|
861
|
+
}
|
|
862
|
+
/** globals whose calls give the same answer for the same input (a clock, a random or the network
|
|
863
|
+
* are not among them: `Date`, `Math`, `performance`, `crypto`, `fetch`) */
|
|
864
|
+
const PURE_GLOBALS = /* @__PURE__ */ new Set([
|
|
865
|
+
"Object",
|
|
866
|
+
"Array",
|
|
867
|
+
"JSON",
|
|
868
|
+
"String",
|
|
869
|
+
"Number",
|
|
870
|
+
"Boolean",
|
|
871
|
+
"Map",
|
|
872
|
+
"Set",
|
|
873
|
+
"structuredClone",
|
|
874
|
+
"parseInt",
|
|
875
|
+
"parseFloat",
|
|
876
|
+
"encodeURIComponent",
|
|
877
|
+
"decodeURIComponent",
|
|
878
|
+
"RegExp",
|
|
879
|
+
"Intl"
|
|
880
|
+
]);
|
|
881
|
+
const NODE_MODULES = /* @__PURE__ */ new Set([
|
|
882
|
+
"crypto",
|
|
883
|
+
"fs",
|
|
884
|
+
"fs/promises",
|
|
885
|
+
"os",
|
|
886
|
+
"path",
|
|
887
|
+
"process",
|
|
888
|
+
"child_process",
|
|
889
|
+
"http",
|
|
890
|
+
"https",
|
|
891
|
+
"net",
|
|
892
|
+
"perf_hooks",
|
|
893
|
+
"worker_threads",
|
|
894
|
+
"async_hooks",
|
|
895
|
+
"util",
|
|
896
|
+
"timers",
|
|
897
|
+
"url",
|
|
898
|
+
"zlib",
|
|
899
|
+
"stream",
|
|
900
|
+
"buffer",
|
|
901
|
+
"events",
|
|
902
|
+
"dns",
|
|
903
|
+
"cluster",
|
|
904
|
+
"v8",
|
|
905
|
+
"vm"
|
|
906
|
+
]);
|
|
907
|
+
/** a module whose exports answer per call or per request, never a value to compute once: Node's own
|
|
908
|
+
* (`randomUUID`, `readFileSync`, `hrtime`) and the framework's request-bound ones (`getRequestEvent`,
|
|
909
|
+
* dynamic env) */
|
|
910
|
+
function per_call_module(from) {
|
|
911
|
+
return from.startsWith("node:") || NODE_MODULES.has(from) || from.startsWith("$app/") || from.startsWith("$env/dynamic/");
|
|
912
|
+
}
|
|
913
|
+
/** the quoted module an `import … from '…'` line names ('' when it names none on this line) */
|
|
914
|
+
function import_source(line) {
|
|
915
|
+
const f = line.lastIndexOf("from");
|
|
916
|
+
const at = f === -1 ? line.indexOf("import") + 6 : f + 4;
|
|
917
|
+
for (let i = at; i < line.length; i++) {
|
|
918
|
+
const q = line[i];
|
|
919
|
+
if (q !== "'" && q !== "\"") continue;
|
|
920
|
+
const e = line.indexOf(q, i + 1);
|
|
921
|
+
return e === -1 ? "" : line.slice(i + 1, e);
|
|
922
|
+
}
|
|
923
|
+
return "";
|
|
924
|
+
}
|
|
925
|
+
/** globals whose every use can answer differently: the network, a clock, the process, timers */
|
|
926
|
+
const PER_CALL_GLOBALS = /* @__PURE__ */ new Set([
|
|
927
|
+
"fetch",
|
|
928
|
+
"performance",
|
|
929
|
+
"crypto",
|
|
930
|
+
"process",
|
|
931
|
+
"globalThis",
|
|
932
|
+
"setTimeout",
|
|
933
|
+
"setInterval",
|
|
934
|
+
"setImmediate",
|
|
935
|
+
"queueMicrotask",
|
|
936
|
+
"require"
|
|
937
|
+
]);
|
|
938
|
+
/** methods that change what their receiver holds */
|
|
939
|
+
const MUTATORS = /* @__PURE__ */ new Set([
|
|
940
|
+
"push",
|
|
941
|
+
"pop",
|
|
942
|
+
"shift",
|
|
943
|
+
"unshift",
|
|
944
|
+
"splice",
|
|
945
|
+
"sort",
|
|
946
|
+
"reverse",
|
|
947
|
+
"fill",
|
|
948
|
+
"copyWithin",
|
|
949
|
+
"set",
|
|
950
|
+
"add",
|
|
951
|
+
"delete",
|
|
952
|
+
"clear"
|
|
953
|
+
]);
|
|
954
|
+
/** the names a line CHANGES without declaring: `x = …`, `x.a.b = …`, `x[k] += …`, `x.list.push(…)`,
|
|
955
|
+
* `x.n++`, `delete x.k`, `Object.assign(x, …)` — the root name of each changed chain */
|
|
956
|
+
function changed_names(t) {
|
|
957
|
+
const out = /* @__PURE__ */ new Set();
|
|
958
|
+
for (let k = 0; k < t.length; k++) {
|
|
959
|
+
const x = t[k];
|
|
960
|
+
if (!is_id_start(x.charCodeAt(0)) || NOT_A_NAME.has(x) || t[k - 1] === "." || t[k - 1] === "?.") continue;
|
|
961
|
+
if (t[k - 1] === "const" || t[k - 1] === "let" || t[k - 1] === "var") continue;
|
|
962
|
+
if (x === "Object" && t[k + 1] === "." && t[k + 2] === "assign" && t[k + 3] === "(" && is_id_start((t[k + 4] ?? "").charCodeAt(0))) out.add(t[k + 4]);
|
|
963
|
+
if (t[k - 1] === "delete") out.add(x);
|
|
964
|
+
let j = k + 1;
|
|
965
|
+
let last = "";
|
|
966
|
+
while (j < t.length) if ((t[j] === "." || t[j] === "?.") && is_id_start((t[j + 1] ?? "").charCodeAt(0))) {
|
|
967
|
+
last = t[j + 1];
|
|
968
|
+
j += 2;
|
|
969
|
+
} else if (t[j] === "[") {
|
|
970
|
+
let d = 0;
|
|
971
|
+
for (; j < t.length; j++) if (t[j] === "[") d++;
|
|
972
|
+
else if (t[j] === "]" && --d === 0) break;
|
|
973
|
+
last = "";
|
|
974
|
+
j++;
|
|
975
|
+
} else break;
|
|
976
|
+
const after = t[j];
|
|
977
|
+
if (after === "=" || after === "+=" || after === "-=" || after === "*=" || after === "/=" || after === "||=" || after === "&&=" || after === "??=" || after === "++" || after === "--") out.add(x);
|
|
978
|
+
else if (after === "(" && j > k + 1 && MUTATORS.has(last)) out.add(x);
|
|
979
|
+
else if (t[k - 1] === "++" || t[k - 1] === "--") out.add(x);
|
|
980
|
+
}
|
|
981
|
+
return [...out];
|
|
982
|
+
}
|
|
983
|
+
function constant_work(source, spans = false) {
|
|
984
|
+
const lines = source.split("\n");
|
|
985
|
+
const T = lines.map((l) => tokens(l));
|
|
986
|
+
const span = load_span(T);
|
|
987
|
+
if (!span) return [];
|
|
988
|
+
const { head, body0, body1 } = span;
|
|
989
|
+
const module_names = /* @__PURE__ */ new Set();
|
|
990
|
+
/** what a per-call module gives (`randomUUID`, `readFileSync`) */
|
|
991
|
+
const per_call = /* @__PURE__ */ new Set();
|
|
992
|
+
/** the file's state: top-level `let` / `var`, and a `const` made with `new` (a cache) */
|
|
993
|
+
const state = /* @__PURE__ */ new Set();
|
|
994
|
+
/** the file's own helpers (a `function`, or a `const` holding one), each with its lines */
|
|
995
|
+
const helpers = [];
|
|
996
|
+
let file_depth = 0;
|
|
997
|
+
for (let i = 0; i < T.length; i++) {
|
|
998
|
+
const t = T[i];
|
|
999
|
+
const at_top = file_depth === 0;
|
|
1000
|
+
for (const x of t) file_depth += x === "{" ? 1 : x === "}" ? -1 : 0;
|
|
1001
|
+
if (!at_top || i >= head && i <= body1) continue;
|
|
1002
|
+
if (t[0] === "import") {
|
|
1003
|
+
let end = i;
|
|
1004
|
+
let text = lines[i];
|
|
1005
|
+
const all = [...t];
|
|
1006
|
+
let open = 0;
|
|
1007
|
+
for (const x of t) open += x === "{" ? 1 : x === "}" ? -1 : 0;
|
|
1008
|
+
while (open > 0 && end + 1 < T.length) {
|
|
1009
|
+
end++;
|
|
1010
|
+
text += "\n" + lines[end];
|
|
1011
|
+
for (const x of T[end]) {
|
|
1012
|
+
all.push(x);
|
|
1013
|
+
open += x === "{" ? 1 : x === "}" ? -1 : 0;
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
i = end;
|
|
1017
|
+
file_depth = 0;
|
|
1018
|
+
const into = per_call_module(import_source(text.split("\n").pop())) ? per_call : module_names;
|
|
1019
|
+
const from = all.indexOf("from");
|
|
1020
|
+
for (let k = 1; k < (from === -1 ? all.length : from); k++) if (is_id_start(all[k].charCodeAt(0)) && all[k] !== "type" && all[k] !== "as" && all[k + 1] !== "as") into.add(all[k]);
|
|
1021
|
+
continue;
|
|
1022
|
+
}
|
|
1023
|
+
const j = t[0] === "export" ? 1 : 0;
|
|
1024
|
+
const k = t[j] === "async" ? j + 1 : j;
|
|
1025
|
+
const name = t[k + 1] ?? "";
|
|
1026
|
+
if ((t[k] === "let" || t[k] === "var") && is_id_start(name.charCodeAt(0))) state.add(name);
|
|
1027
|
+
if (t[k] === "const" && t.includes("new")) {
|
|
1028
|
+
if (is_id_start(name.charCodeAt(0))) state.add(name);
|
|
1029
|
+
continue;
|
|
1030
|
+
}
|
|
1031
|
+
if ((t[k] === "const" || t[k] === "function" || t[k] === "class") && is_id_start(name.charCodeAt(0))) {
|
|
1032
|
+
module_names.add(name);
|
|
1033
|
+
let to = i;
|
|
1034
|
+
const opens = (x) => x === "{" || x === "(" || x === "[" ? 1 : x === "}" || x === ")" || x === "]" ? -1 : 0;
|
|
1035
|
+
let d = 0;
|
|
1036
|
+
for (const x of t) d += opens(x);
|
|
1037
|
+
const continues = (row) => {
|
|
1038
|
+
const last = row[row.length - 1];
|
|
1039
|
+
return last === "=>" || last === "=" || last === "(" || last === "," || last === "?" || last === ":" || last === "&&" || last === "||" || last === "??";
|
|
1040
|
+
};
|
|
1041
|
+
while ((d > 0 || continues(T[to])) && to + 1 < T.length) {
|
|
1042
|
+
to++;
|
|
1043
|
+
for (const x of T[to]) d += opens(x);
|
|
1044
|
+
}
|
|
1045
|
+
if (t[k] !== "class" && (t[k] === "function" || t.includes("=>"))) helpers.push({
|
|
1046
|
+
name,
|
|
1047
|
+
from: i,
|
|
1048
|
+
to
|
|
1049
|
+
});
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
const impure = (t) => {
|
|
1053
|
+
for (let k = 0; k < t.length; k++) {
|
|
1054
|
+
const x = t[k];
|
|
1055
|
+
if (t[k - 1] === "." || t[k - 1] === "?.") continue;
|
|
1056
|
+
if (per_call.has(x) || PER_CALL_GLOBALS.has(x)) return true;
|
|
1057
|
+
if (x === "Math" && t[k + 2] === "random") return true;
|
|
1058
|
+
if (x === "Date" && (t[k + 2] === "now" || t[k - 1] === "new" && t[k + 1] === "(" && t[k + 2] === ")")) return true;
|
|
1059
|
+
if (helpers.some((h) => h.name === x) && !module_names.has(x)) return true;
|
|
1060
|
+
}
|
|
1061
|
+
return changed_names(t).some((n) => state.has(n));
|
|
1062
|
+
};
|
|
1063
|
+
for (let dropped = true; dropped;) {
|
|
1064
|
+
dropped = false;
|
|
1065
|
+
for (const h of helpers) {
|
|
1066
|
+
if (!module_names.has(h.name)) continue;
|
|
1067
|
+
for (let i = h.from; i <= h.to; i++) if (impure(T[i])) {
|
|
1068
|
+
module_names.delete(h.name);
|
|
1069
|
+
dropped = true;
|
|
1070
|
+
break;
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
const changed = /* @__PURE__ */ new Set();
|
|
1075
|
+
for (let i = body0 + 1; i < body1; i++) for (const n of changed_names(T[i])) changed.add(n);
|
|
1076
|
+
const local = /* @__PURE__ */ new Set();
|
|
1077
|
+
for (let i = body0 + 1; i < body1; i++) for (const n of line_rw(lines[i]).declared) local.add(n);
|
|
1078
|
+
for (let i = head; i <= body0; i++) for (const x of T[i]) if (is_id_start(x.charCodeAt(0)) && !NOT_A_NAME.has(x) && x !== "load" && x !== "export" && x !== "const" && x !== "async" && x !== "function") local.add(x);
|
|
1079
|
+
/** a read the load can compute once: a constant line of its own, or the module's (or a pure
|
|
1080
|
+
* global) name that nothing in the load shadows */
|
|
1081
|
+
const known = (r) => constant.has(r) || (module_names.has(r) || PURE_GLOBALS.has(r)) && !local.has(r);
|
|
1082
|
+
let constant = /* @__PURE__ */ new Set();
|
|
1083
|
+
let out = [];
|
|
1084
|
+
/** every line (0-based) of the constant statements, continuation lines included */
|
|
1085
|
+
let stmt_lines = /* @__PURE__ */ new Set();
|
|
1086
|
+
for (let grew = true; grew;) {
|
|
1087
|
+
grew = false;
|
|
1088
|
+
constant = /* @__PURE__ */ new Set();
|
|
1089
|
+
out = [];
|
|
1090
|
+
stmt_lines = /* @__PURE__ */ new Set();
|
|
1091
|
+
let depth = 0;
|
|
1092
|
+
for (let i = body0 + 1; i < body1; i++) {
|
|
1093
|
+
const t = T[i];
|
|
1094
|
+
const top = depth === 0;
|
|
1095
|
+
for (const x of t) depth += x === "{" || x === "(" || x === "[" ? 1 : x === "}" || x === ")" || x === "]" ? -1 : 0;
|
|
1096
|
+
if (top && t[0] === "return" && t[1] === "{") {
|
|
1097
|
+
let d = 0;
|
|
1098
|
+
for (let j = i + 1; j < body1; j++) {
|
|
1099
|
+
const e = T[j];
|
|
1100
|
+
const at_top = d === 0;
|
|
1101
|
+
for (const x of e) d += x === "{" || x === "(" || x === "[" ? 1 : x === ")" || x === "]" || x === "}" ? -1 : 0;
|
|
1102
|
+
if (d < 0) break;
|
|
1103
|
+
if (!at_top || d !== 0 || !is_id_start((e[0] ?? "").charCodeAt(0)) || e[1] !== ":" || e.includes("await")) continue;
|
|
1104
|
+
const text = lines[j].slice(lines[j].indexOf(":") + 1);
|
|
1105
|
+
const rw = line_rw(text);
|
|
1106
|
+
if (!rw.reads.length || !rw.reads.every(known)) continue;
|
|
1107
|
+
const et = tokens(text);
|
|
1108
|
+
const calls = rw.reads.filter((r) => module_names.has(r) && et.some((x, k) => x === r && et[k + 1] === "("));
|
|
1109
|
+
if (calls.length) {
|
|
1110
|
+
if (!spans) out.push({
|
|
1111
|
+
line: j + 1,
|
|
1112
|
+
names: [e[0]],
|
|
1113
|
+
calls
|
|
1114
|
+
});
|
|
1115
|
+
stmt_lines.add(j);
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
break;
|
|
1119
|
+
}
|
|
1120
|
+
if (!top || t[0] !== "const") continue;
|
|
1121
|
+
let open = 0;
|
|
1122
|
+
for (const x of t) open += x === "(" || x === "[" || x === "{" ? 1 : x === ")" || x === "]" || x === "}" ? -1 : 0;
|
|
1123
|
+
let text = lines[i];
|
|
1124
|
+
const all = [...t];
|
|
1125
|
+
for (let j = i + 1; open > 0 && j < body1; j++) {
|
|
1126
|
+
text += "\n" + lines[j];
|
|
1127
|
+
for (const x of T[j]) {
|
|
1128
|
+
all.push(x);
|
|
1129
|
+
open += x === "(" || x === "[" || x === "{" ? 1 : x === ")" || x === "]" || x === "}" ? -1 : 0;
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
if (open !== 0 || all.includes("await")) continue;
|
|
1133
|
+
const rw = line_rw(text.split("\n").join(" "));
|
|
1134
|
+
if (!rw.declared.length || !rw.reads.length) continue;
|
|
1135
|
+
if (!rw.reads.every(known)) continue;
|
|
1136
|
+
if (rw.declared.some((n) => changed.has(n))) {
|
|
1137
|
+
for (const r of rw.reads) if (constant.has(r) && !changed.has(r)) {
|
|
1138
|
+
changed.add(r);
|
|
1139
|
+
grew = true;
|
|
1140
|
+
}
|
|
1141
|
+
continue;
|
|
1142
|
+
}
|
|
1143
|
+
const calls = rw.reads.filter((r) => module_names.has(r) && all.some((x, k) => x === r && all[k + 1] === "("));
|
|
1144
|
+
for (const n of rw.declared) constant.add(n);
|
|
1145
|
+
const end = i + text.split("\n").length;
|
|
1146
|
+
if (all.includes("(")) {
|
|
1147
|
+
out.push({
|
|
1148
|
+
line: i + 1,
|
|
1149
|
+
names: rw.declared,
|
|
1150
|
+
calls,
|
|
1151
|
+
...spans ? {
|
|
1152
|
+
end,
|
|
1153
|
+
work: true
|
|
1154
|
+
} : {}
|
|
1155
|
+
});
|
|
1156
|
+
for (let j = i; j < end; j++) stmt_lines.add(j);
|
|
1157
|
+
} else if (spans) out.push({
|
|
1158
|
+
line: i + 1,
|
|
1159
|
+
end,
|
|
1160
|
+
names: rw.declared,
|
|
1161
|
+
calls: [],
|
|
1162
|
+
work: false
|
|
1163
|
+
});
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
const mine = stmt_lines;
|
|
1167
|
+
for (const o of out) o.calls = o.calls.filter((c) => !T.some((t, i) => i > body0 && i < body1 && !mine.has(i) && t.includes(c)));
|
|
1168
|
+
return out;
|
|
1169
|
+
}
|
|
1170
|
+
/**
|
|
1171
|
+
* THE MOVE, CHECKED: which of the load's constant statements go to the file's top level (the costly
|
|
1172
|
+
* ones and what they read, nothing else) and the line they go above (the load's `export`, 1-based).
|
|
1173
|
+
* Null when moving them could change what the file does:
|
|
1174
|
+
* - the file declares a top-level `const` / `let` / `var` / `class` AFTER the load. A moved call can
|
|
1175
|
+
* run a helper that reads it before it is set (its temporal dead zone), so no line is safe to move
|
|
1176
|
+
* above it;
|
|
1177
|
+
* - a moved name is already a top-level name of the file (an import, a const, a function).
|
|
1178
|
+
*/
|
|
1179
|
+
function hoist_plan(source) {
|
|
1180
|
+
const all = constant_work(source, true);
|
|
1181
|
+
if (!all.some((x) => x.work)) return null;
|
|
1182
|
+
const lines = source.split("\n");
|
|
1183
|
+
const T = lines.map((l) => tokens(l));
|
|
1184
|
+
const span = load_span(T);
|
|
1185
|
+
if (!span) return null;
|
|
1186
|
+
const by_name = /* @__PURE__ */ new Map();
|
|
1187
|
+
for (const x of all) for (const n of x.names) by_name.set(n, x);
|
|
1188
|
+
const keep = /* @__PURE__ */ new Set();
|
|
1189
|
+
const visit = (x) => {
|
|
1190
|
+
if (keep.has(x)) return;
|
|
1191
|
+
keep.add(x);
|
|
1192
|
+
for (const r of line_rw(lines.slice(x.line - 1, x.end).join(" ")).reads) {
|
|
1193
|
+
const y = by_name.get(r);
|
|
1194
|
+
if (y) visit(y);
|
|
1195
|
+
}
|
|
1196
|
+
};
|
|
1197
|
+
for (const x of all) if (x.work) visit(x);
|
|
1198
|
+
const stmts = all.filter((x) => keep.has(x)).map((x) => ({
|
|
1199
|
+
line: x.line,
|
|
1200
|
+
end: x.end,
|
|
1201
|
+
names: x.names
|
|
1202
|
+
}));
|
|
1203
|
+
const top = /* @__PURE__ */ new Set();
|
|
1204
|
+
let late = false;
|
|
1205
|
+
let depth = 0;
|
|
1206
|
+
for (let i = 0; i < T.length; i++) {
|
|
1207
|
+
const t = T[i];
|
|
1208
|
+
const at_top = depth === 0;
|
|
1209
|
+
for (const x of t) depth += x === "{" ? 1 : x === "}" ? -1 : 0;
|
|
1210
|
+
if (!at_top || i >= span.head && i <= span.body1) continue;
|
|
1211
|
+
if (t[0] === "import") {
|
|
1212
|
+
const ids = [];
|
|
1213
|
+
let j = i;
|
|
1214
|
+
for (let d = 0; j < T.length; j++) {
|
|
1215
|
+
for (const x of T[j]) if (x === "{") d++;
|
|
1216
|
+
else if (x === "}") d--;
|
|
1217
|
+
else ids.push(x);
|
|
1218
|
+
if (d <= 0) break;
|
|
1219
|
+
}
|
|
1220
|
+
for (let k = 0; k < ids.length; k++) if (is_id_start(ids[k].charCodeAt(0)) && !NOT_A_NAME.has(ids[k]) && ids[k] !== "import" && ids[k] !== "from" && ids[k] !== "type" && ids[k] !== "as" && ids[k + 1] !== "as") top.add(ids[k]);
|
|
1221
|
+
continue;
|
|
1222
|
+
}
|
|
1223
|
+
let k = t[0] === "export" ? 1 : 0;
|
|
1224
|
+
if (t[k] === "declare") k++;
|
|
1225
|
+
if (t[k] === "const" || t[k] === "let" || t[k] === "var") {
|
|
1226
|
+
for (const n of line_rw(lines[i]).declared) top.add(n);
|
|
1227
|
+
if (i > span.body1) late = true;
|
|
1228
|
+
} else if (t[k] === "class") {
|
|
1229
|
+
if (t[k + 1]) top.add(t[k + 1]);
|
|
1230
|
+
if (i > span.body1) late = true;
|
|
1231
|
+
} else if (t[k] === "function" || t[k] === "async" && t[k + 1] === "function") {
|
|
1232
|
+
const n = t[k] === "async" ? t[k + 2] : t[k + 1];
|
|
1233
|
+
if (n && n !== "*") top.add(n);
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
if (late || stmts.some((x) => x.names.some((n) => top.has(n)))) return null;
|
|
1237
|
+
return {
|
|
1238
|
+
above: span.head + 1,
|
|
1239
|
+
stmts
|
|
1240
|
+
};
|
|
1241
|
+
}
|
|
1242
|
+
/** `data` at token `k` is the page's data: bare `data.x` (the prop), or `page.data.x` / `$page.data.x`
|
|
1243
|
+
* — not another object's `data` (`post.data.tags`) */
|
|
1244
|
+
function is_page_data(t, k) {
|
|
1245
|
+
if (t[k - 1] !== ".") return true;
|
|
1246
|
+
return t[k - 2] === "page" || t[k - 2] === "$page";
|
|
1247
|
+
}
|
|
1248
|
+
/** the page-data key a component's local is made from: `const products = data.catalog.…` → catalog */
|
|
1249
|
+
function local_key(lines, name) {
|
|
1250
|
+
for (const l of lines) {
|
|
1251
|
+
const t = tokens(l);
|
|
1252
|
+
const d = t.findIndex((x, k) => (x === "const" || x === "let") && t[k + 1] === name);
|
|
1253
|
+
if (d === -1) continue;
|
|
1254
|
+
for (let j = d + 2; j < t.length; j++) if (t[j] === "data" && t[j + 1] === "." && is_id_start((t[j + 2] ?? "").charCodeAt(0)) && is_page_data(t, j)) return t[j + 2];
|
|
1255
|
+
return;
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
/**
|
|
1259
|
+
* THE LOOPS IN A COMPONENT'S MARKUP: each `{#each … as …}` block, the page-data key it walks
|
|
1260
|
+
* (`data.results`, `page.data.results` → results), and the components rendered inside it
|
|
1261
|
+
* (`<ResultCard …>`, nested blocks included). What decides how many times a child renders.
|
|
1262
|
+
* 1-based lines.
|
|
1263
|
+
*/
|
|
1264
|
+
function each_blocks(svelte) {
|
|
1265
|
+
const lines = svelte.split("\n");
|
|
1266
|
+
const out = [];
|
|
1267
|
+
const open = [];
|
|
1268
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1269
|
+
const t = tokens(lines[i]);
|
|
1270
|
+
for (let k = 0; k < t.length; k++) if (t[k] === "{" && t[k + 1] === "#" && t[k + 2] === "each") {
|
|
1271
|
+
let key;
|
|
1272
|
+
for (let j = k + 3; j < t.length && t[j] !== "as"; j++) if (t[j] === "data" && t[j + 1] === "." && is_id_start((t[j + 2] ?? "").charCodeAt(0)) && is_page_data(t, j)) key = t[j + 2];
|
|
1273
|
+
if (!key && is_id_start((t[k + 3] ?? "").charCodeAt(0)) && t[k + 4] === "as") key = local_key(lines, t[k + 3]);
|
|
1274
|
+
out.push({
|
|
1275
|
+
line: i + 1,
|
|
1276
|
+
...key ? { key } : {},
|
|
1277
|
+
components: /* @__PURE__ */ new Set()
|
|
1278
|
+
});
|
|
1279
|
+
open.push(out.length - 1);
|
|
1280
|
+
} else if (t[k] === "{" && t[k + 1] === "/" && t[k + 2] === "each") open.pop();
|
|
1281
|
+
else if (t[k] === "<" && open.length && (t[k + 1] ?? "").charCodeAt(0) >= 65 && (t[k + 1] ?? "").charCodeAt(0) <= 90) for (const o of open) out[o].components.add(t[k + 1]);
|
|
1282
|
+
}
|
|
1283
|
+
return out.map((o) => ({
|
|
1284
|
+
line: o.line,
|
|
1285
|
+
...o.key ? { key: o.key } : {},
|
|
1286
|
+
components: [...o.components]
|
|
1287
|
+
}));
|
|
1288
|
+
}
|
|
1289
|
+
/**
|
|
1290
|
+
* DATA ONLY A LATE ISLAND NEEDS: in a page's markup, the page-data keys used ONLY as props of
|
|
1291
|
+
* islands imported to wake late (`import Reviews from './Reviews.svelte' with { wake: 'visible' }`
|
|
1292
|
+
* — or `idle`, `interaction`), never in the page's own markup or an eager island. The page waits
|
|
1293
|
+
* for that data on every request for something seen later, if at all. An island already
|
|
1294
|
+
* `render: 'deferred'` fetches later by itself and is not one. 1-based lines of each use.
|
|
1295
|
+
*/
|
|
1296
|
+
function late_island_keys(svelte) {
|
|
1297
|
+
const lines = svelte.split("\n");
|
|
1298
|
+
const late = /* @__PURE__ */ new Map();
|
|
1299
|
+
for (const l of lines) {
|
|
1300
|
+
const t = tokens(l);
|
|
1301
|
+
if (t[0] !== "import" || !t.includes("with")) continue;
|
|
1302
|
+
const name = t[1];
|
|
1303
|
+
const w = t.indexOf("wake");
|
|
1304
|
+
const deferred = t.includes("render");
|
|
1305
|
+
const m = w !== -1 ? l.slice(l.indexOf("wake")) : "";
|
|
1306
|
+
const word = [
|
|
1307
|
+
"visible",
|
|
1308
|
+
"idle",
|
|
1309
|
+
"interaction"
|
|
1310
|
+
].find((x) => m.includes(`'${x}'`) || m.includes(`"${x}"`));
|
|
1311
|
+
if (name && word && !deferred) late.set(name, word);
|
|
1312
|
+
}
|
|
1313
|
+
if (!late.size) return [];
|
|
1314
|
+
const uses = /* @__PURE__ */ new Map();
|
|
1315
|
+
let tag;
|
|
1316
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1317
|
+
const t = tokens(lines[i]);
|
|
1318
|
+
for (let k = 0; k < t.length; k++) {
|
|
1319
|
+
if (t[k] === "<" && is_id_start((t[k + 1] ?? "").charCodeAt(0))) tag = t[k + 1];
|
|
1320
|
+
else if (t[k] === ">") tag = void 0;
|
|
1321
|
+
if (t[k] === "data" && t[k + 1] === "." && is_id_start((t[k + 2] ?? "").charCodeAt(0)) && is_page_data(t, k)) {
|
|
1322
|
+
const list = uses.get(t[k + 2]) ?? [];
|
|
1323
|
+
list.push({
|
|
1324
|
+
...tag && late.has(tag) ? { island: tag } : {},
|
|
1325
|
+
line: i + 1
|
|
1326
|
+
});
|
|
1327
|
+
uses.set(t[k + 2], list);
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
const out = [];
|
|
1332
|
+
for (const [key, list] of uses) {
|
|
1333
|
+
if (!list.every((u) => u.island)) continue;
|
|
1334
|
+
out.push({
|
|
1335
|
+
key,
|
|
1336
|
+
island: list[0].island,
|
|
1337
|
+
wake: late.get(list[0].island),
|
|
1338
|
+
line: list[0].line
|
|
1339
|
+
});
|
|
1340
|
+
}
|
|
1341
|
+
return out;
|
|
1342
|
+
}
|
|
1343
|
+
/** What a `load` takes from the request: the names in its parameter list (`async ({ url, cookies })`
|
|
1344
|
+
* → url, cookies). Empty when it takes nothing; null when there is no load. */
|
|
1345
|
+
function load_inputs(source) {
|
|
1346
|
+
const lines = source.split("\n");
|
|
1347
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1348
|
+
const t = tokens(lines[i]);
|
|
1349
|
+
if (t[0] !== "export" || !t.some((x, k) => x === "load" && (t[k - 1] === "const" || t[k - 1] === "function"))) continue;
|
|
1350
|
+
const all = [];
|
|
1351
|
+
for (let j = i; j < Math.min(lines.length, i + 20); j++) all.push(...tokens(lines[j]));
|
|
1352
|
+
let k = all.indexOf("load");
|
|
1353
|
+
if (all[k - 1] === "function") while (k < all.length && all[k] !== "(") k++;
|
|
1354
|
+
else {
|
|
1355
|
+
const arrow = all.indexOf("=>", k);
|
|
1356
|
+
if (arrow === -1 || all[arrow - 1] !== ")") return arrow > 0 && is_id_start((all[arrow - 1] ?? "").charCodeAt(0)) ? ["*" + all[arrow - 1]] : [];
|
|
1357
|
+
let back = 0;
|
|
1358
|
+
for (k = arrow - 1; k > 0; k--) if (all[k] === ")") back++;
|
|
1359
|
+
else if (all[k] === "(" && --back === 0) break;
|
|
1360
|
+
}
|
|
1361
|
+
let d = 0;
|
|
1362
|
+
const names = [];
|
|
1363
|
+
for (; k < all.length; k++) {
|
|
1364
|
+
const x = all[k];
|
|
1365
|
+
if (x === "(" || x === "{" || x === "[") d++;
|
|
1366
|
+
else if (x === ")" || x === "}" || x === "]") {
|
|
1367
|
+
if (--d === 0) break;
|
|
1368
|
+
} else if (d === 1 && is_id_start(x.charCodeAt(0)) && all[k - 1] === "(") names.push("*" + x);
|
|
1369
|
+
else if (d === 2 && is_id_start(x.charCodeAt(0)) && (all[k - 1] === "{" || all[k - 1] === ",")) names.push(x);
|
|
1370
|
+
}
|
|
1371
|
+
return names;
|
|
1372
|
+
}
|
|
1373
|
+
return null;
|
|
1374
|
+
}
|
|
1375
|
+
/**
|
|
1376
|
+
* THE CHANGE, WRITTEN: awaits in a row as one `Promise.all` — when they are plain one-line
|
|
1377
|
+
* statements (`const user = await get('user');`), next to each other (blank and comment lines
|
|
1378
|
+
* between are fine; any other statement between could feed a later call, so none is), the rewrite
|
|
1379
|
+
* only moves the later calls up to start with the first. The caller has already proved no call
|
|
1380
|
+
* reads what an earlier one produced. `lines`: the source from the first line to the last (1-based
|
|
1381
|
+
* `start`); `at`: the chain's lines. Undefined when the shape is anything else.
|
|
1382
|
+
*/
|
|
1383
|
+
function promise_all_rewrite(lines, start, at) {
|
|
1384
|
+
if (at.length < 2) return void 0;
|
|
1385
|
+
const set = new Set(at);
|
|
1386
|
+
const first = Math.min(...at);
|
|
1387
|
+
const last = Math.max(...at);
|
|
1388
|
+
const decls = [];
|
|
1389
|
+
let indent = "";
|
|
1390
|
+
let kind = "const";
|
|
1391
|
+
for (let n = first; n <= last; n++) {
|
|
1392
|
+
const raw = lines[n - start];
|
|
1393
|
+
if (raw === void 0) return void 0;
|
|
1394
|
+
const l = raw.trim();
|
|
1395
|
+
if (!set.has(n)) {
|
|
1396
|
+
if (l === "" || l.startsWith("//")) continue;
|
|
1397
|
+
return;
|
|
1398
|
+
}
|
|
1399
|
+
const d = await_decl(l);
|
|
1400
|
+
if (!d) return void 0;
|
|
1401
|
+
if (d.kind === "let") kind = "let";
|
|
1402
|
+
if (n === first) indent = raw.slice(0, raw.length - raw.trimStart().length);
|
|
1403
|
+
decls.push(d);
|
|
1404
|
+
}
|
|
1405
|
+
return {
|
|
1406
|
+
before: lines.slice(first - start, last - start + 1).join("\n"),
|
|
1407
|
+
after: `${indent}${kind} [${decls.map((d) => d.binding).join(", ")}] = await Promise.all([${decls.map((d) => d.expr).join(", ")}]);`
|
|
1408
|
+
};
|
|
1409
|
+
}
|
|
1410
|
+
/** where a line's code ends: before a `//` or `/*` comment that is not inside a string */
|
|
1411
|
+
function code_end(s) {
|
|
1412
|
+
for (let i = 0; i < s.length; i++) {
|
|
1413
|
+
const c = s[i];
|
|
1414
|
+
if (c === "\"" || c === "'" || c === "`") {
|
|
1415
|
+
for (i++; i < s.length && s[i] !== c; i++) if (s[i] === "\\") i++;
|
|
1416
|
+
continue;
|
|
1417
|
+
}
|
|
1418
|
+
if (c === "/" && (s[i + 1] === "/" || s[i + 1] === "*")) return i;
|
|
1419
|
+
}
|
|
1420
|
+
return s.length;
|
|
1421
|
+
}
|
|
1422
|
+
/** names that say a call CHANGES something (`db.insert`, `saveCart`): two of them started together
|
|
1423
|
+
* can land in either order, so a chain with one stays in a row */
|
|
1424
|
+
const WRITE_VERBS = [
|
|
1425
|
+
"insert",
|
|
1426
|
+
"update",
|
|
1427
|
+
"delete",
|
|
1428
|
+
"remove",
|
|
1429
|
+
"save",
|
|
1430
|
+
"create",
|
|
1431
|
+
"put",
|
|
1432
|
+
"post",
|
|
1433
|
+
"patch",
|
|
1434
|
+
"write",
|
|
1435
|
+
"send",
|
|
1436
|
+
"set",
|
|
1437
|
+
"add",
|
|
1438
|
+
"upsert",
|
|
1439
|
+
"increment",
|
|
1440
|
+
"decrement",
|
|
1441
|
+
"mark",
|
|
1442
|
+
"lock",
|
|
1443
|
+
"commit",
|
|
1444
|
+
"publish",
|
|
1445
|
+
"enqueue",
|
|
1446
|
+
"push",
|
|
1447
|
+
"reserve",
|
|
1448
|
+
"charge"
|
|
1449
|
+
];
|
|
1450
|
+
/**
|
|
1451
|
+
* One statement `const <binding> = await <call>;` split into its binding and call, or undefined when
|
|
1452
|
+
* the line is any other shape: a type on the binding (`const a: User = …`), two names (`const a =
|
|
1453
|
+
* …, b = …`), a second statement or an `await` inside the call, anything done to the answer
|
|
1454
|
+
* (`await f() + 1`, `await f() || x`: moved into an array, the operator would apply to the promise),
|
|
1455
|
+
* or a call whose name says it writes.
|
|
1456
|
+
*/
|
|
1457
|
+
function await_decl(line) {
|
|
1458
|
+
let body = line.slice(0, code_end(line)).trim();
|
|
1459
|
+
if (body.endsWith(";")) body = body.slice(0, -1).trimEnd();
|
|
1460
|
+
const kind = body.startsWith("const ") ? "const" : body.startsWith("let ") ? "let" : void 0;
|
|
1461
|
+
if (!kind) return void 0;
|
|
1462
|
+
let eq = -1;
|
|
1463
|
+
for (let i = kind.length, d = 0; i < body.length && eq < 0; i++) {
|
|
1464
|
+
const c = body[i];
|
|
1465
|
+
if (c === "\"" || c === "'" || c === "`") {
|
|
1466
|
+
for (i++; i < body.length && body[i] !== c; i++) if (body[i] === "\\") i++;
|
|
1467
|
+
} else if (c === "(" || c === "[" || c === "{") d++;
|
|
1468
|
+
else if (c === ")" || c === "]" || c === "}") d--;
|
|
1469
|
+
else if (d === 0 && (c === ":" || c === "," || c === ";")) return void 0;
|
|
1470
|
+
else if (d === 0 && c === "=" && body[i + 1] !== "=" && body[i + 1] !== ">") eq = i;
|
|
1471
|
+
}
|
|
1472
|
+
if (eq < 0) return void 0;
|
|
1473
|
+
const binding = body.slice(kind.length, eq).trim();
|
|
1474
|
+
const rest = body.slice(eq + 1).trim();
|
|
1475
|
+
if (!binding || !rest.startsWith("await ")) return void 0;
|
|
1476
|
+
const expr = rest.slice(6).trim();
|
|
1477
|
+
const e = tokens(expr);
|
|
1478
|
+
if (e.includes("await") || e.includes(";")) return void 0;
|
|
1479
|
+
let j = e[0] === "new" ? 1 : 0;
|
|
1480
|
+
if (!is_id_start((e[j] ?? "").charCodeAt(0))) return void 0;
|
|
1481
|
+
const names = [e[j++]];
|
|
1482
|
+
let called = false;
|
|
1483
|
+
while (j < e.length) if ((e[j] === "." || e[j] === "?.") && is_id_start((e[j + 1] ?? "").charCodeAt(0))) {
|
|
1484
|
+
names.push(e[j + 1]);
|
|
1485
|
+
j += 2;
|
|
1486
|
+
called = false;
|
|
1487
|
+
} else if (e[j] === "(" || e[j] === "[") {
|
|
1488
|
+
called = e[j] === "(";
|
|
1489
|
+
let d = 0;
|
|
1490
|
+
for (; j < e.length; j++) if (e[j] === "(" || e[j] === "[" || e[j] === "{") d++;
|
|
1491
|
+
else if ((e[j] === ")" || e[j] === "]" || e[j] === "}") && --d === 0) break;
|
|
1492
|
+
if (d !== 0) return void 0;
|
|
1493
|
+
j++;
|
|
1494
|
+
} else return void 0;
|
|
1495
|
+
if (!called) return void 0;
|
|
1496
|
+
const says_write = (n) => WRITE_VERBS.some((v) => {
|
|
1497
|
+
if (!n.toLowerCase().startsWith(v)) return false;
|
|
1498
|
+
const next = n[v.length];
|
|
1499
|
+
return next === void 0 || next === "_" || next >= "A" && next <= "Z";
|
|
1500
|
+
});
|
|
1501
|
+
if (names.some(says_write)) return void 0;
|
|
1502
|
+
if (e.includes("method") && !expr.includes("'GET'") && !expr.includes("\"GET\"")) return void 0;
|
|
1503
|
+
return {
|
|
1504
|
+
kind,
|
|
1505
|
+
binding,
|
|
1506
|
+
expr
|
|
1507
|
+
};
|
|
1508
|
+
}
|
|
1509
|
+
/** the balanced `( … )` text starting at `open` (the index of `(`), and the index past its `)` */
|
|
1510
|
+
function paren_text(s, open) {
|
|
1511
|
+
let d = 0;
|
|
1512
|
+
for (let i = open; i < s.length; i++) {
|
|
1513
|
+
const c = s[i];
|
|
1514
|
+
if (c === "\"" || c === "'" || c === "`") {
|
|
1515
|
+
const q = c;
|
|
1516
|
+
for (i++; i < s.length && s[i] !== q; i++) if (s[i] === "\\") i++;
|
|
1517
|
+
continue;
|
|
1518
|
+
}
|
|
1519
|
+
if (c === "(") d++;
|
|
1520
|
+
else if (c === ")" && --d === 0) return {
|
|
1521
|
+
inner: s.slice(open + 1, i),
|
|
1522
|
+
end: i + 1
|
|
1523
|
+
};
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
/**
|
|
1527
|
+
* THE CHANGE, WRITTEN, for a formatter built on every call: `new Intl.NumberFormat(locale, {…})` on
|
|
1528
|
+
* a line. Arguments that read nothing (literals only) → one formatter at the top of the file, used
|
|
1529
|
+
* on the line. Arguments that read variables → a cached factory at the top (one formatter per
|
|
1530
|
+
* locale + options, built once) called on the line in place of `new Intl.X(`. A `.localeCompare(y,
|
|
1531
|
+
* 'de', {…})` with literal arguments → one `Intl.Collator`'s `compare`. `ts`: the file is TypeScript
|
|
1532
|
+
* (the factory is typed). Undefined when the line is anything else.
|
|
1533
|
+
*/
|
|
1534
|
+
function formatter_rewrite(line, opts) {
|
|
1535
|
+
const { ts, svelte = false } = opts;
|
|
1536
|
+
const taken = opts.taken ?? new Set(tokens(line));
|
|
1537
|
+
const made = opts.made ?? /* @__PURE__ */ new Map();
|
|
1538
|
+
const edits = [];
|
|
1539
|
+
const tops = [];
|
|
1540
|
+
const reused = /* @__PURE__ */ new Set();
|
|
1541
|
+
/** the name for one addition: the one already made for the same thing, else a fresh one no name
|
|
1542
|
+
* of the file uses (`COLLATOR`, `COLLATOR_2`); `decl` writes the addition under that name */
|
|
1543
|
+
const name_for = (key, base, decl, also = () => []) => {
|
|
1544
|
+
const had = made.get(key);
|
|
1545
|
+
if (had) {
|
|
1546
|
+
reused.add(had);
|
|
1547
|
+
return had;
|
|
1548
|
+
}
|
|
1549
|
+
let name = base;
|
|
1550
|
+
for (let n = 2; taken.has(name) || also(name).some((x) => taken.has(x)); n++) name = `${base}_${n}`;
|
|
1551
|
+
taken.add(name);
|
|
1552
|
+
for (const x of also(name)) taken.add(x);
|
|
1553
|
+
made.set(key, name);
|
|
1554
|
+
tops.push(decl(name));
|
|
1555
|
+
return name;
|
|
1556
|
+
};
|
|
1557
|
+
for (let at = code_index(line, "new Intl."); at !== -1;) {
|
|
1558
|
+
let k = at + 9;
|
|
1559
|
+
while (k < line.length && is_id_part(line.charCodeAt(k))) k++;
|
|
1560
|
+
const ctor = line.slice(at + 9, k);
|
|
1561
|
+
const args = line[k] === "(" ? paren_text(line, k) : void 0;
|
|
1562
|
+
if (!ctor || !args) return void 0;
|
|
1563
|
+
let snake = "";
|
|
1564
|
+
for (let i = 0; i < ctor.length; i++) {
|
|
1565
|
+
const c = ctor[i];
|
|
1566
|
+
snake += c >= "A" && c <= "Z" ? (i ? "_" : "") + c.toLowerCase() : c;
|
|
1567
|
+
}
|
|
1568
|
+
if (!line_rw(`x(${args.inner})`).reads.filter((r) => r !== "x").length) {
|
|
1569
|
+
const name = name_for(`const|${ctor}|${args.inner}`, snake.toUpperCase(), (n) => `const ${n} = new Intl.${ctor}(${args.inner});`);
|
|
1570
|
+
edits.push({
|
|
1571
|
+
start: at,
|
|
1572
|
+
end: args.end,
|
|
1573
|
+
text: name
|
|
1574
|
+
});
|
|
1575
|
+
} else {
|
|
1576
|
+
const shape = FACTORY_CTORS.get(ctor);
|
|
1577
|
+
if (!shape) {
|
|
1578
|
+
at = code_index(line, "new Intl.", args.end);
|
|
1579
|
+
continue;
|
|
1580
|
+
}
|
|
1581
|
+
const types = ts ? {
|
|
1582
|
+
loc: ": Intl.LocalesArgument",
|
|
1583
|
+
opt: shape.required ? `: Intl.${ctor}Options` : `?: Intl.${ctor}Options`,
|
|
1584
|
+
map: `<string, Intl.${ctor}>`
|
|
1585
|
+
} : {
|
|
1586
|
+
loc: "",
|
|
1587
|
+
opt: "",
|
|
1588
|
+
map: ""
|
|
1589
|
+
};
|
|
1590
|
+
const name = name_for(`factory|${ctor}`, snake, (n) => [
|
|
1591
|
+
`const ${n}_cache = new Map${types.map}();`,
|
|
1592
|
+
`function ${n}(locale${types.loc}, options${types.opt}) {`,
|
|
1593
|
+
`\tconst key = \`\${String(locale)}|\${JSON.stringify(options)}\`;`,
|
|
1594
|
+
`\tlet f = ${n}_cache.get(key);`,
|
|
1595
|
+
`\tif (!f) ${n}_cache.set(key, (f = new Intl.${ctor}(locale, options)));`,
|
|
1596
|
+
`\treturn f;`,
|
|
1597
|
+
`}`
|
|
1598
|
+
].join("\n"), (n) => [`${n}_cache`]);
|
|
1599
|
+
edits.push({
|
|
1600
|
+
start: at,
|
|
1601
|
+
end: args.end,
|
|
1602
|
+
text: `${name}(${args.inner})`
|
|
1603
|
+
});
|
|
1604
|
+
}
|
|
1605
|
+
at = code_index(line, "new Intl.", args.end);
|
|
1606
|
+
}
|
|
1607
|
+
for (let lc = code_index(line, ".localeCompare("); lc !== -1;) {
|
|
1608
|
+
const args = paren_text(line, lc + 14);
|
|
1609
|
+
if (!args) return void 0;
|
|
1610
|
+
const parts = split_args(args.inner);
|
|
1611
|
+
const next = code_index(line, ".localeCompare(", args.end);
|
|
1612
|
+
if (line[lc - 1] === "?") {
|
|
1613
|
+
lc = next;
|
|
1614
|
+
continue;
|
|
1615
|
+
}
|
|
1616
|
+
if (parts.length < 2) {
|
|
1617
|
+
lc = next;
|
|
1618
|
+
continue;
|
|
1619
|
+
}
|
|
1620
|
+
const other = parts[0];
|
|
1621
|
+
const rest = parts.slice(1).join(", ");
|
|
1622
|
+
if (line_rw(`x(${rest})`).reads.some((r) => r !== "x")) {
|
|
1623
|
+
lc = next;
|
|
1624
|
+
continue;
|
|
1625
|
+
}
|
|
1626
|
+
let s = lc - 1;
|
|
1627
|
+
while (s >= 0) {
|
|
1628
|
+
const c = line[s];
|
|
1629
|
+
if (is_id_part(c.charCodeAt(0)) || c === "." || c === "?" && line[s + 1] === ".") s--;
|
|
1630
|
+
else if (c === ")" || c === "]") {
|
|
1631
|
+
const open = c === ")" ? "(" : "[";
|
|
1632
|
+
let d = 0;
|
|
1633
|
+
for (; s >= 0; s--) if (line[s] === c) d++;
|
|
1634
|
+
else if (line[s] === open && --d === 0) break;
|
|
1635
|
+
s--;
|
|
1636
|
+
} else break;
|
|
1637
|
+
}
|
|
1638
|
+
const recv = line.slice(s + 1, lc);
|
|
1639
|
+
if (!recv || !is_id_start(recv.charCodeAt(0)) || edits.some((e) => e.start < args.end && s + 1 < e.end)) {
|
|
1640
|
+
lc = next;
|
|
1641
|
+
continue;
|
|
1642
|
+
}
|
|
1643
|
+
const name = name_for(`collator|${rest}`, "COLLATOR", (n) => `const ${n} = new Intl.Collator(${rest});`);
|
|
1644
|
+
edits.push({
|
|
1645
|
+
start: s + 1,
|
|
1646
|
+
end: args.end,
|
|
1647
|
+
text: `${name}.compare(${recv}, ${other})`
|
|
1648
|
+
});
|
|
1649
|
+
lc = next;
|
|
1650
|
+
}
|
|
1651
|
+
if (!edits.length) return void 0;
|
|
1652
|
+
edits.sort((a, b) => a.start - b.start);
|
|
1653
|
+
let changed = "";
|
|
1654
|
+
let from = 0;
|
|
1655
|
+
for (const e of edits) {
|
|
1656
|
+
changed += line.slice(from, e.start) + e.text;
|
|
1657
|
+
from = e.end;
|
|
1658
|
+
}
|
|
1659
|
+
changed += line.slice(from);
|
|
1660
|
+
const where = svelte ? "in `<script module>` (built once, not per render)" : "at the top of the file (built once)";
|
|
1661
|
+
return {
|
|
1662
|
+
before: line,
|
|
1663
|
+
after: `${[...tops.length ? [`// ${where}:`, ...tops] : [], ...reused.size ? [`// uses ${[...reused].join(", ")}, added by the change above`] : []].join("\n")}\n// the line:\n${changed}`
|
|
1664
|
+
};
|
|
1665
|
+
}
|
|
1666
|
+
/** Intl constructors taken as `(locales, options)` — what one cached factory can build — and
|
|
1667
|
+
* whether their options are required */
|
|
1668
|
+
const FACTORY_CTORS = /* @__PURE__ */ new Map([
|
|
1669
|
+
["NumberFormat", { required: false }],
|
|
1670
|
+
["DateTimeFormat", { required: false }],
|
|
1671
|
+
["RelativeTimeFormat", { required: false }],
|
|
1672
|
+
["PluralRules", { required: false }],
|
|
1673
|
+
["Collator", { required: false }],
|
|
1674
|
+
["ListFormat", { required: false }],
|
|
1675
|
+
["Segmenter", { required: false }],
|
|
1676
|
+
["DisplayNames", { required: true }]
|
|
1677
|
+
]);
|
|
1678
|
+
/** the index of `needle` in a line's code, at or after `from`: not inside a string, not in a
|
|
1679
|
+
* comment. -1 when none. */
|
|
1680
|
+
function code_index(line, needle, from = 0) {
|
|
1681
|
+
const end = code_end(line);
|
|
1682
|
+
for (let i = from; i < end; i++) {
|
|
1683
|
+
const c = line[i];
|
|
1684
|
+
if (c === "\"" || c === "'" || c === "`") {
|
|
1685
|
+
for (i++; i < end && line[i] !== c; i++) if (line[i] === "\\") i++;
|
|
1686
|
+
continue;
|
|
1687
|
+
}
|
|
1688
|
+
if (line.startsWith(needle, i)) return i;
|
|
1689
|
+
}
|
|
1690
|
+
return -1;
|
|
1691
|
+
}
|
|
1692
|
+
/** a call's argument text split at its top-level commas (not one inside a call, an object or a string) */
|
|
1693
|
+
function split_args(inner) {
|
|
1694
|
+
const out = [];
|
|
1695
|
+
let d = 0;
|
|
1696
|
+
let from = 0;
|
|
1697
|
+
for (let i = 0; i < inner.length; i++) {
|
|
1698
|
+
const c = inner[i];
|
|
1699
|
+
if (c === "\"" || c === "'" || c === "`") {
|
|
1700
|
+
for (i++; i < inner.length && inner[i] !== c; i++) if (inner[i] === "\\") i++;
|
|
1701
|
+
} else if (c === "(" || c === "[" || c === "{") d++;
|
|
1702
|
+
else if (c === ")" || c === "]" || c === "}") d--;
|
|
1703
|
+
else if (c === "," && d === 0) {
|
|
1704
|
+
out.push(inner.slice(from, i).trim());
|
|
1705
|
+
from = i + 1;
|
|
1706
|
+
}
|
|
1707
|
+
}
|
|
1708
|
+
const last = inner.slice(from).trim();
|
|
1709
|
+
if (last) out.push(last);
|
|
1710
|
+
return out;
|
|
1711
|
+
}
|
|
1712
|
+
/** The functions a set of lines names — called (`newestReview(x)`) or handed over (`.sort(byName)`):
|
|
1713
|
+
* every bare identifier on them (not a property, not a keyword). What a CPU row by that name, run
|
|
1714
|
+
* from a helper file with no line of the load on its samples, was working for. */
|
|
1715
|
+
function names_on(source, lines) {
|
|
1716
|
+
const all = source.split("\n");
|
|
1717
|
+
const out = /* @__PURE__ */ new Set();
|
|
1718
|
+
for (const l of lines) {
|
|
1719
|
+
const t = tokens(all[l - 1] ?? "");
|
|
1720
|
+
for (let k = 0; k < t.length; k++) if (is_id_start(t[k].charCodeAt(0)) && !NOT_A_NAME.has(t[k]) && t[k - 1] !== "." && t[k - 1] !== "?.") out.add(t[k]);
|
|
1721
|
+
}
|
|
1722
|
+
return [...out];
|
|
1723
|
+
}
|
|
1724
|
+
export { REGEXP_FRAME, backtracks, calls_name, constant_work, each_blocks, formatter_rewrite, function_body, handle_parts, hoist_plan, import_aliases, key_sources, late_island_keys, line_rw, load_inputs, load_span, names_on, needs_previous, parent_use, promise_all_rewrite, regex_literals, tokens, wrapper_aliases };
|