@narumitw/pi-chrome-devtools 0.53.4 → 0.54.1

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.
Files changed (31) hide show
  1. package/CHANGELOG.md +109 -0
  2. package/README.md +34 -16
  3. package/dist/chunks/{browser-settings-menu-FVMCCUPM.ts → browser-settings-menu-7M3VM7QN.ts} +25 -3
  4. package/dist/chunks/browser-settings-menu-7M3VM7QN.ts.map +7 -0
  5. package/dist/chunks/{chunk-V7N3WI5B.ts → chunk-JKPJYUVG.ts} +340 -108
  6. package/dist/chunks/chunk-JKPJYUVG.ts.map +7 -0
  7. package/dist/chunks/{chunk-4B3EQHOH.ts → chunk-JLEJ6WFO.ts} +2 -2
  8. package/dist/chunks/{chunk-LSJSDY2M.ts → chunk-WTMZID3G.ts} +382 -246
  9. package/dist/chunks/chunk-WTMZID3G.ts.map +7 -0
  10. package/dist/chunks/{menu-MTPSLDHZ.ts → menu-HGG26E3X.ts} +7 -7
  11. package/dist/chunks/{menu-MTPSLDHZ.ts.map → menu-HGG26E3X.ts.map} +1 -1
  12. package/dist/chunks/{tools-OVNH45IJ.ts → tools-O5N4KIAK.ts} +3 -3
  13. package/dist/index.ts +45 -21
  14. package/dist/index.ts.map +2 -2
  15. package/docs/browser-setup.md +7 -1
  16. package/package.json +6 -5
  17. package/src/browser-candidates.ts +184 -0
  18. package/src/browser-manager.ts +93 -276
  19. package/src/browser-settings-menu.ts +23 -0
  20. package/src/browser-startup.ts +122 -0
  21. package/src/chrome-devtools.ts +48 -15
  22. package/src/lazy-tools.ts +358 -48
  23. package/src/menu.ts +3 -3
  24. package/src/runtime.ts +2 -2
  25. package/src/settings.ts +65 -1
  26. package/src/tool-selector.ts +95 -40
  27. package/dist/chunks/browser-settings-menu-FVMCCUPM.ts.map +0 -7
  28. package/dist/chunks/chunk-LSJSDY2M.ts.map +0 -7
  29. package/dist/chunks/chunk-V7N3WI5B.ts.map +0 -7
  30. /package/dist/chunks/{chunk-4B3EQHOH.ts.map → chunk-JLEJ6WFO.ts.map} +0 -0
  31. /package/dist/chunks/{tools-OVNH45IJ.ts.map → tools-O5N4KIAK.ts.map} +0 -0
@@ -0,0 +1,184 @@
1
+ import { constants } from "node:fs";
2
+ import { delimiter, isAbsolute, join, resolve } from "node:path";
3
+ import type { BrowserCandidate, BrowserCandidateDefinition } from "./runtime.js";
4
+ import type { EffectiveBrowserSettings } from "./settings.js";
5
+
6
+ function normalizePathForComparison(value: string) {
7
+ return process.platform === "win32" ? value.toLowerCase() : value;
8
+ }
9
+
10
+ export function browserCandidateDefinitions(browser: EffectiveBrowserSettings): BrowserCandidateDefinition[] {
11
+ const explicitCandidate = explicitBrowserCandidateDefinition(browser);
12
+ if (explicitCandidate.length > 0) return explicitCandidate;
13
+
14
+ return uniqueBrowserCandidates(platformBrowserCandidateDefinitions());
15
+ }
16
+
17
+ function explicitBrowserCandidateDefinition(browser: EffectiveBrowserSettings): BrowserCandidateDefinition[] {
18
+ if (!browser.executablePath) return [];
19
+ return [
20
+ {
21
+ label: browser.executablePathSource === "environment" ? "PI_CHROME_DEVTOOLS_BROWSER" : "browser.executablePath",
22
+ executable: browser.executablePath,
23
+ source: "env",
24
+ },
25
+ ];
26
+ }
27
+
28
+ function platformBrowserCandidateDefinitions(): BrowserCandidateDefinition[] {
29
+ if (process.platform === "darwin") {
30
+ return [
31
+ {
32
+ label: "Google Chrome",
33
+ executable: "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
34
+ source: "wellKnownPath",
35
+ },
36
+ {
37
+ label: "Chromium",
38
+ executable: "/Applications/Chromium.app/Contents/MacOS/Chromium",
39
+ source: "wellKnownPath",
40
+ },
41
+ {
42
+ label: "Brave Browser",
43
+ executable: "/Applications/Brave Browser.app/Contents/MacOS/Brave Browser",
44
+ source: "wellKnownPath",
45
+ },
46
+ {
47
+ label: "Microsoft Edge",
48
+ executable: "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge",
49
+ source: "wellKnownPath",
50
+ },
51
+ ];
52
+ }
53
+
54
+ if (process.platform === "win32") {
55
+ return windowsBrowserCandidateDefinitions();
56
+ }
57
+
58
+ return [
59
+ { label: "Google Chrome", executable: "google-chrome", source: "path" },
60
+ { label: "Google Chrome Stable", executable: "google-chrome-stable", source: "path" },
61
+ { label: "Chromium", executable: "chromium", source: "path" },
62
+ { label: "Chromium Browser", executable: "chromium-browser", source: "path" },
63
+ { label: "Brave Browser", executable: "brave-browser", source: "path" },
64
+ { label: "Brave", executable: "brave", source: "path" },
65
+ { label: "Microsoft Edge", executable: "microsoft-edge", source: "path" },
66
+ { label: "Microsoft Edge Stable", executable: "microsoft-edge-stable", source: "path" },
67
+ ];
68
+ }
69
+
70
+ function windowsBrowserCandidateDefinitions(): BrowserCandidateDefinition[] {
71
+ const programFiles = [process.env.PROGRAMFILES, process.env["PROGRAMFILES(X86)"], process.env.LOCALAPPDATA].filter(
72
+ (value): value is string => typeof value === "string" && value.length > 0,
73
+ );
74
+ const wellKnownPaths = programFiles.flatMap((root) => [
75
+ join(root, "Google", "Chrome", "Application", "chrome.exe"),
76
+ join(root, "Chromium", "Application", "chrome.exe"),
77
+ join(root, "BraveSoftware", "Brave-Browser", "Application", "brave.exe"),
78
+ join(root, "Microsoft", "Edge", "Application", "msedge.exe"),
79
+ ]);
80
+ return [
81
+ ...wellKnownPaths.map((executable) => ({
82
+ label: browserLabelFromExecutable(executable),
83
+ executable,
84
+ source: "wellKnownPath" as const,
85
+ })),
86
+ { label: "Google Chrome", executable: "chrome.exe", source: "path" },
87
+ { label: "Chromium", executable: "chromium.exe", source: "path" },
88
+ { label: "Brave Browser", executable: "brave.exe", source: "path" },
89
+ { label: "Microsoft Edge", executable: "msedge.exe", source: "path" },
90
+ ];
91
+ }
92
+
93
+ function browserLabelFromExecutable(executable: string) {
94
+ const normalizedExecutable = normalizePathForComparison(executable);
95
+ if (normalizedExecutable.includes("brave")) return "Brave Browser";
96
+ if (normalizedExecutable.includes("edge") || normalizedExecutable.includes("msedge")) {
97
+ return "Microsoft Edge";
98
+ }
99
+ if (normalizedExecutable.includes("chromium")) return "Chromium";
100
+ return "Google Chrome";
101
+ }
102
+
103
+ function uniqueBrowserCandidates(candidates: BrowserCandidateDefinition[]) {
104
+ const seen = new Set<string>();
105
+ return candidates.filter((candidate) => {
106
+ const key = normalizePathForComparison(candidate.executable);
107
+ if (seen.has(key)) return false;
108
+ seen.add(key);
109
+ return true;
110
+ });
111
+ }
112
+
113
+ export async function resolveBrowserCandidates(
114
+ definitions: BrowserCandidateDefinition[],
115
+ access: (path: string, mode: number) => Promise<void>,
116
+ throwIfCancelled: () => void,
117
+ ) {
118
+ const candidates: BrowserCandidate[] = [];
119
+ for (const definition of definitions) {
120
+ const resolvedExecutable = await resolveBrowserExecutable(definition.executable, access, throwIfCancelled);
121
+ throwIfCancelled();
122
+ if (!resolvedExecutable) continue;
123
+ candidates.push({ ...definition, resolvedExecutable });
124
+ }
125
+ return uniqueBrowserCandidatesByResolvedPath(candidates);
126
+ }
127
+
128
+ function uniqueBrowserCandidatesByResolvedPath(candidates: BrowserCandidate[]) {
129
+ const seen = new Set<string>();
130
+ return candidates.filter((candidate) => {
131
+ const key = normalizePathForComparison(resolve(candidate.resolvedExecutable));
132
+ if (seen.has(key)) return false;
133
+ seen.add(key);
134
+ return true;
135
+ });
136
+ }
137
+
138
+ async function resolveBrowserExecutable(
139
+ executable: string,
140
+ access: (path: string, mode: number) => Promise<void>,
141
+ throwIfCancelled: () => void,
142
+ ) {
143
+ if (hasPathSeparator(executable) || isAbsolute(executable)) {
144
+ const resolvedExecutable = isAbsolute(executable) ? executable : resolve(executable);
145
+ return (await canAccessExecutable(resolvedExecutable, access, throwIfCancelled)) ? resolvedExecutable : undefined;
146
+ }
147
+
148
+ for (const directory of executableSearchPath()) {
149
+ for (const executableName of executableSearchNames(executable)) {
150
+ const candidate = join(directory, executableName);
151
+ if (await canAccessExecutable(candidate, access, throwIfCancelled)) return candidate;
152
+ }
153
+ }
154
+ return undefined;
155
+ }
156
+
157
+ function hasPathSeparator(path: string) {
158
+ return path.includes("/") || path.includes("\\");
159
+ }
160
+
161
+ function executableSearchPath() {
162
+ return (process.env.PATH ?? "").split(delimiter).filter((part) => part.length > 0);
163
+ }
164
+
165
+ function executableSearchNames(executable: string) {
166
+ if (process.platform !== "win32" || /\.[a-z0-9]+$/i.test(executable)) return [executable];
167
+ return [executable, `${executable}.exe`, `${executable}.cmd`, `${executable}.bat`];
168
+ }
169
+
170
+ async function canAccessExecutable(
171
+ path: string,
172
+ access: (path: string, mode: number) => Promise<void>,
173
+ throwIfCancelled: () => void,
174
+ ) {
175
+ let accessible = false;
176
+ try {
177
+ await access(path, process.platform === "win32" ? constants.F_OK : constants.X_OK);
178
+ accessible = true;
179
+ } catch {
180
+ // An absent or inaccessible executable is not a browser candidate.
181
+ }
182
+ throwIfCancelled();
183
+ return accessible;
184
+ }