@kernocal/viteshot 0.1.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.
- package/LICENSE +21 -0
- package/README.md +12 -0
- package/assets/dashboard.ts +197 -0
- package/dist/capture-extension-screenshots-DQWqq69W.mjs +180 -0
- package/dist/cli.mjs +77 -0
- package/dist/config-BvHGePNd.mjs +264 -0
- package/dist/config-Cs-Dm27M.mjs +4 -0
- package/dist/create-server-CpPbF7ms.mjs +14 -0
- package/dist/create-server-DwNGwEEV.mjs +5 -0
- package/dist/export-popup-screenshot-Dp7G2Ylf.mjs +3 -0
- package/dist/export-popup-screenshot-UhxUtKCP.mjs +145 -0
- package/dist/export-screenshots-B6IGCV4J.mjs +92 -0
- package/dist/export-screenshots-B77VFVWT.mjs +4 -0
- package/dist/get-locales-DgKRepYE.mjs +26 -0
- package/dist/help-messages-B_-FA6OE.mjs +73 -0
- package/dist/index.d.mts +112 -0
- package/dist/index.mjs +7 -0
- package/dist/init-BUrgR61N.mjs +63 -0
- package/package.json +62 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Aaron
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# @kernocal/viteshot
|
|
2
|
+
|
|
3
|
+
Generate store screenshots and promo images with HTML, powered by Vite.
|
|
4
|
+
|
|
5
|
+
> Fork of [@aklinker1/viteshot](https://github.com/aklinker1/viteshot)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
## lots of stuff is hardcoded for me, sorry!
|
|
9
|
+
|
|
10
|
+
you can probably change it in `src/core/config.ts`
|
|
11
|
+
|
|
12
|
+
also it uses pnpm now careful
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/** Main JS module for displaying the different screenshots */
|
|
2
|
+
import screenshots from "viteshot-virtual/screenshots";
|
|
3
|
+
import locales from "viteshot-virtual/locales";
|
|
4
|
+
|
|
5
|
+
declare const app: HTMLDivElement;
|
|
6
|
+
|
|
7
|
+
// Icons
|
|
8
|
+
|
|
9
|
+
const LOCALE_FLAGS: Record<string, string> = {
|
|
10
|
+
be: "🇧🇪",
|
|
11
|
+
br: "🇧🇷",
|
|
12
|
+
de: "🇩🇪",
|
|
13
|
+
en: "🇺🇸",
|
|
14
|
+
en_gb: "🇬🇧",
|
|
15
|
+
en_us: "🇺🇸",
|
|
16
|
+
es: "🇪🇸",
|
|
17
|
+
es_mx: "🇲🇽",
|
|
18
|
+
fr: "🇫🇷",
|
|
19
|
+
it: "🇮🇹",
|
|
20
|
+
ja: "🇯🇵",
|
|
21
|
+
pt: "🇧🇷",
|
|
22
|
+
pt_br: "🇧🇷",
|
|
23
|
+
ru: "🇷🇺",
|
|
24
|
+
zh: "🇨🇳",
|
|
25
|
+
zh_tw: "🇹🇼",
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const HEROICONS_ARROW_UP_RIGHT_16_SOLID = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><!-- Icon from HeroIcons by Refactoring UI Inc - https://github.com/tailwindlabs/heroicons/blob/master/LICENSE --><path fill="currentColor" fill-rule="evenodd" d="M4.22 11.78a.75.75 0 0 1 0-1.06L9.44 5.5H5.75a.75.75 0 0 1 0-1.5h5.5a.75.75 0 0 1 .75.75v5.5a.75.75 0 0 1-1.5 0V6.56l-5.22 5.22a.75.75 0 0 1-1.06 0" clip-rule="evenodd"/></svg>`;
|
|
29
|
+
const HEROICONS_SUN = `<svg class="light" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><!-- Icon from HeroIcons by Refactoring UI Inc - https://github.com/tailwindlabs/heroicons/blob/master/LICENSE --><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M12 3v2.25m6.364.386l-1.591 1.591M21 12h-2.25m-.386 6.364l-1.591-1.591M12 18.75V21m-4.773-4.227l-1.591 1.591M5.25 12H3m4.227-4.773L5.636 5.636M15.75 12a3.75 3.75 0 1 1-7.5 0a3.75 3.75 0 0 1 7.5 0"/></svg>`;
|
|
30
|
+
const HEROICONS_MOON = `<svg class="dark" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><!-- Icon from HeroIcons by Refactoring UI Inc - https://github.com/tailwindlabs/heroicons/blob/master/LICENSE --><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M21.752 15.002A9.7 9.7 0 0 1 18 15.75A9.75 9.75 0 0 1 8.25 6c0-1.33.266-2.597.748-3.752A9.75 9.75 0 0 0 3 11.25A9.75 9.75 0 0 0 12.75 21a9.75 9.75 0 0 0 9.002-5.998"/></svg>`;
|
|
31
|
+
|
|
32
|
+
// Language management
|
|
33
|
+
|
|
34
|
+
const LANGUAGE_STORAGE_KEY = "viteshot:language";
|
|
35
|
+
|
|
36
|
+
let currentLanguageId: string | undefined =
|
|
37
|
+
getStoredLanguage() ?? locales[0]?.id;
|
|
38
|
+
function getStoredLanguage() {
|
|
39
|
+
const prevId = localStorage.getItem(LANGUAGE_STORAGE_KEY);
|
|
40
|
+
if (!prevId || !locales.some((l) => l.id === prevId)) return;
|
|
41
|
+
|
|
42
|
+
return prevId;
|
|
43
|
+
}
|
|
44
|
+
function setLanguage(languageId: string): void {
|
|
45
|
+
currentLanguageId = languageId;
|
|
46
|
+
localStorage.setItem(LANGUAGE_STORAGE_KEY, languageId);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Theme
|
|
50
|
+
|
|
51
|
+
const THEME_STORAGE_KEY = "viteshot:theme";
|
|
52
|
+
|
|
53
|
+
let currentTheme: string = getStoredTheme() ?? "dark";
|
|
54
|
+
|
|
55
|
+
function getStoredTheme() {
|
|
56
|
+
const prevTheme = localStorage.getItem(THEME_STORAGE_KEY);
|
|
57
|
+
if (prevTheme !== "light" && prevTheme !== "dark") return;
|
|
58
|
+
|
|
59
|
+
return prevTheme;
|
|
60
|
+
}
|
|
61
|
+
function toggleTheme(): void {
|
|
62
|
+
const theme = currentTheme === "dark" ? "light" : "dark";
|
|
63
|
+
|
|
64
|
+
currentTheme = theme;
|
|
65
|
+
updateTheme();
|
|
66
|
+
localStorage.setItem(THEME_STORAGE_KEY, theme);
|
|
67
|
+
}
|
|
68
|
+
function updateTheme() {
|
|
69
|
+
document.documentElement.setAttribute("data-theme", currentTheme);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// UI Rendering
|
|
73
|
+
|
|
74
|
+
type Child = string | HTMLElement | SVGElement | false | undefined | null;
|
|
75
|
+
|
|
76
|
+
interface ElementTagMap extends HTMLElementTagNameMap {
|
|
77
|
+
svg: SVGElement;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function h<TTag extends keyof ElementTagMap>(
|
|
81
|
+
tag: TTag,
|
|
82
|
+
children: Child[],
|
|
83
|
+
): ElementTagMap[TTag];
|
|
84
|
+
function h<TTag extends keyof ElementTagMap>(
|
|
85
|
+
tag: TTag,
|
|
86
|
+
props: Partial<ElementTagMap[TTag]>,
|
|
87
|
+
children?: Child[],
|
|
88
|
+
): ElementTagMap[TTag];
|
|
89
|
+
function h<TTag extends keyof ElementTagMap>(
|
|
90
|
+
tag: TTag,
|
|
91
|
+
arg1?: any,
|
|
92
|
+
arg2?: any,
|
|
93
|
+
): ElementTagMap[TTag] {
|
|
94
|
+
const hasProps =
|
|
95
|
+
typeof arg1 === "object" &&
|
|
96
|
+
!Array.isArray(arg1) &&
|
|
97
|
+
!(arg1 instanceof HTMLElement);
|
|
98
|
+
const props: Record<string, any> = hasProps ? arg1 : undefined;
|
|
99
|
+
const children: Child[] = hasProps ? arg2 : arg1;
|
|
100
|
+
|
|
101
|
+
const el = document.createElement(tag) as ElementTagMap[TTag];
|
|
102
|
+
|
|
103
|
+
if (props) {
|
|
104
|
+
for (const [key, value] of Object.entries(props)) {
|
|
105
|
+
if (key in el) (el as any)[key] = value;
|
|
106
|
+
else el.setAttribute(key, value);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (children) {
|
|
111
|
+
for (const child of children) {
|
|
112
|
+
if (child != null && child !== false) el.append(child);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return el;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function svg(outerHtml: string): SVGElement {
|
|
120
|
+
const temp = document.createElement("div");
|
|
121
|
+
temp.innerHTML = outerHtml;
|
|
122
|
+
return temp.firstElementChild as SVGElement;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function renderHeader() {
|
|
126
|
+
app.append(
|
|
127
|
+
h("div", { className: "header" }, [
|
|
128
|
+
h("div", { className: "left" }, [
|
|
129
|
+
h("h1", "ViteShot"),
|
|
130
|
+
h(
|
|
131
|
+
"a",
|
|
132
|
+
{ href: "https://github.com/aklinker1/viteshot", target: "_blank" },
|
|
133
|
+
[h("span", ["Docs"]), svg(HEROICONS_ARROW_UP_RIGHT_16_SOLID)],
|
|
134
|
+
),
|
|
135
|
+
]),
|
|
136
|
+
locales.length > 0 &&
|
|
137
|
+
h(
|
|
138
|
+
"select",
|
|
139
|
+
{
|
|
140
|
+
className: "language-select",
|
|
141
|
+
onchange: (e) => {
|
|
142
|
+
setLanguage((e.target as HTMLSelectElement).value);
|
|
143
|
+
renderScreenshots();
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
locales.map((l) =>
|
|
147
|
+
h("option", { value: l.id, selected: currentLanguageId === l.id }, [
|
|
148
|
+
`${LOCALE_FLAGS[l.language.replaceAll("-", "_").toLowerCase()] || "🌐"} ${l.language}`,
|
|
149
|
+
]),
|
|
150
|
+
),
|
|
151
|
+
),
|
|
152
|
+
h("button", { className: "theme-toggle", onclick: toggleTheme }, [
|
|
153
|
+
svg(HEROICONS_SUN),
|
|
154
|
+
svg(HEROICONS_MOON),
|
|
155
|
+
]),
|
|
156
|
+
]),
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function renderScreenshots() {
|
|
161
|
+
const existingUl = app.querySelector("& > ul");
|
|
162
|
+
|
|
163
|
+
const newUl = h(
|
|
164
|
+
"ul",
|
|
165
|
+
screenshots.map((ss) =>
|
|
166
|
+
h("li", { id: ss.id, className: "list-item" }, [
|
|
167
|
+
h("h2", { className: "title-row" }, [
|
|
168
|
+
h("a", { className: "name", href: `#${ss.id}` }, [ss.name]),
|
|
169
|
+
ss.size && h("span", " "),
|
|
170
|
+
ss.size && h("span", { className: "size" }, [ss.size]),
|
|
171
|
+
]),
|
|
172
|
+
|
|
173
|
+
h("div", { className: "iframe-wrapper" }, [
|
|
174
|
+
h(
|
|
175
|
+
"iframe",
|
|
176
|
+
{
|
|
177
|
+
width: String(ss.width),
|
|
178
|
+
height: String(ss.height),
|
|
179
|
+
src: `/screenshot/${currentLanguageId ? encodeURIComponent(currentLanguageId) : "null"}/${encodeURIComponent(ss.id)}.html`,
|
|
180
|
+
},
|
|
181
|
+
[],
|
|
182
|
+
),
|
|
183
|
+
]),
|
|
184
|
+
]),
|
|
185
|
+
),
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
if (existingUl) {
|
|
189
|
+
existingUl.replaceWith(newUl);
|
|
190
|
+
} else {
|
|
191
|
+
app.append(newUl);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
updateTheme();
|
|
196
|
+
renderHeader();
|
|
197
|
+
renderScreenshots();
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { t as getLocales } from "./get-locales-DgKRepYE.mjs";
|
|
2
|
+
import { join, relative, resolve } from "node:path";
|
|
3
|
+
import { readdir } from "node:fs/promises";
|
|
4
|
+
import natsort from "natural-compare-lite";
|
|
5
|
+
import { styleText } from "node:util";
|
|
6
|
+
import puppeteer from "puppeteer-core";
|
|
7
|
+
|
|
8
|
+
//#region src/core/get-screenshots.ts
|
|
9
|
+
const FILENAME_REGEX = /^(?<name>.*?)@(?<size>(?<width>[0-9]+)x(?<height>[0-9]+)).(?<ext>.*)$/;
|
|
10
|
+
async function getScreenshots(designsDir) {
|
|
11
|
+
return (await readdir(designsDir, {
|
|
12
|
+
recursive: true,
|
|
13
|
+
withFileTypes: true
|
|
14
|
+
})).filter((file) => file.isFile()).map((file) => {
|
|
15
|
+
const match = FILENAME_REGEX.exec(file.name);
|
|
16
|
+
if (!match) return;
|
|
17
|
+
const path = join(file.parentPath, file.name);
|
|
18
|
+
const name = match.groups.name;
|
|
19
|
+
const size = match.groups.size;
|
|
20
|
+
const width = Number(match.groups.width);
|
|
21
|
+
const height = Number(match.groups.height);
|
|
22
|
+
const ext = match.groups.ext;
|
|
23
|
+
return {
|
|
24
|
+
id: relative(designsDir, path),
|
|
25
|
+
path,
|
|
26
|
+
ext,
|
|
27
|
+
name,
|
|
28
|
+
size,
|
|
29
|
+
width,
|
|
30
|
+
height
|
|
31
|
+
};
|
|
32
|
+
}).filter((file) => file != null).toSorted((a, b) => natsort(a.id, b.id));
|
|
33
|
+
}
|
|
34
|
+
async function logInvalidDesignFiles(designsDir) {
|
|
35
|
+
const invalid = (await readdir(designsDir, {
|
|
36
|
+
recursive: true,
|
|
37
|
+
withFileTypes: true
|
|
38
|
+
})).filter((file) => file.isFile()).map((file) => FILENAME_REGEX.exec(file.name) ? void 0 : file).filter((file) => file != null).map((file) => file.name);
|
|
39
|
+
if (invalid.length > 0) console.warn(`${styleText(["bold", "yellow"], "Invalid design file names:")}\n - ${invalid.join("\n - ")}`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
//#endregion
|
|
43
|
+
//#region src/core/capture-extension-screenshots.ts
|
|
44
|
+
/**
|
|
45
|
+
* Capture popup/sidebar/options screenshots for a single locale.
|
|
46
|
+
* Pass `language: "_default"` (or omit) to capture without `--lang`.
|
|
47
|
+
*/
|
|
48
|
+
async function captureLocale(options) {
|
|
49
|
+
const language = options.language ?? "_default";
|
|
50
|
+
const waitSelector = options.waitSelector ?? "body";
|
|
51
|
+
const settleDelay = options.settleDelay ?? 300;
|
|
52
|
+
if (language !== "_default") console.log(` \x1b[1m[${language}]\x1b[0m Capturing extension screenshots...`);
|
|
53
|
+
else console.log(` Capturing extension screenshots...`);
|
|
54
|
+
let browser;
|
|
55
|
+
try {
|
|
56
|
+
browser = await puppeteer.launch({
|
|
57
|
+
executablePath: options.chromePath,
|
|
58
|
+
headless: false,
|
|
59
|
+
args: [
|
|
60
|
+
`--disable-extensions-except=${options.extensionPath}`,
|
|
61
|
+
`--load-extension=${options.extensionPath}`,
|
|
62
|
+
"--window-size=1280,800",
|
|
63
|
+
...language !== "_default" ? [`--lang=${language}`] : []
|
|
64
|
+
]
|
|
65
|
+
});
|
|
66
|
+
const page = (await browser.pages())[0] ?? await browser.newPage();
|
|
67
|
+
await page.goto("data:text/html,<h1>viteshot</h1>");
|
|
68
|
+
const client = await page.createCDPSession();
|
|
69
|
+
const extensionId = await new Promise((resolve, reject) => {
|
|
70
|
+
const timeout = setTimeout(() => reject(/* @__PURE__ */ new Error("Timed out waiting for extension to load")), 2e4);
|
|
71
|
+
const poll = async () => {
|
|
72
|
+
const { targetInfos } = await client.send("Target.getTargets");
|
|
73
|
+
const ext = targetInfos.find((t) => t.url.startsWith("chrome-extension://"));
|
|
74
|
+
if (ext) {
|
|
75
|
+
clearTimeout(timeout);
|
|
76
|
+
resolve(new URL(ext.url).hostname);
|
|
77
|
+
} else setTimeout(poll, 200);
|
|
78
|
+
};
|
|
79
|
+
poll();
|
|
80
|
+
});
|
|
81
|
+
await client.detach();
|
|
82
|
+
console.log(` Extension ID: ${extensionId}`);
|
|
83
|
+
await page.goto("https://example.com", {
|
|
84
|
+
waitUntil: "domcontentloaded",
|
|
85
|
+
timeout: 2e4
|
|
86
|
+
});
|
|
87
|
+
await new Promise((r) => setTimeout(r, 1e3));
|
|
88
|
+
const captured = {};
|
|
89
|
+
let hasPopup = false;
|
|
90
|
+
let hasSidebar = false;
|
|
91
|
+
try {
|
|
92
|
+
const worker = await (await browser.waitForTarget((target) => target.type() === "service_worker" && target.url().includes(`chrome-extension://${extensionId}`), { timeout: 1e4 })).worker();
|
|
93
|
+
if (!worker) throw new Error("Service worker not available");
|
|
94
|
+
await worker.evaluate("chrome.action.openPopup()");
|
|
95
|
+
const popupPage = await (await browser.waitForTarget((target) => target.type() === "page" && target.url().includes(`chrome-extension://${extensionId}`), { timeout: 5e3 })).asPage();
|
|
96
|
+
await popupPage.waitForSelector(waitSelector);
|
|
97
|
+
await new Promise((r) => setTimeout(r, settleDelay));
|
|
98
|
+
captured.popup = await popupPage.screenshot({
|
|
99
|
+
type: "png",
|
|
100
|
+
fullPage: true
|
|
101
|
+
});
|
|
102
|
+
hasPopup = true;
|
|
103
|
+
console.log(` ✅ popup captured`);
|
|
104
|
+
} catch {
|
|
105
|
+
console.log(" ⚠ No popup action found");
|
|
106
|
+
}
|
|
107
|
+
try {
|
|
108
|
+
const sidebarPage = await browser.newPage();
|
|
109
|
+
const sidePanelUrl = `chrome-extension://${extensionId}/sidepanel.html`;
|
|
110
|
+
const response = await sidebarPage.goto(sidePanelUrl, {
|
|
111
|
+
waitUntil: "domcontentloaded",
|
|
112
|
+
timeout: 5e3
|
|
113
|
+
});
|
|
114
|
+
if (!response || !response.ok()) throw new Error("No sidebar");
|
|
115
|
+
await sidebarPage.waitForSelector(waitSelector);
|
|
116
|
+
await new Promise((r) => setTimeout(r, settleDelay));
|
|
117
|
+
captured.sidebar = await sidebarPage.screenshot({
|
|
118
|
+
type: "png",
|
|
119
|
+
fullPage: true
|
|
120
|
+
});
|
|
121
|
+
hasSidebar = true;
|
|
122
|
+
console.log(` ✅ sidebar captured`);
|
|
123
|
+
} catch {
|
|
124
|
+
console.log(" ⚠ No sidebar found");
|
|
125
|
+
}
|
|
126
|
+
if (!hasPopup && !hasSidebar) console.warn("\n \x1B[33m⚠⚠⚠ WARNING: Extension has neither a popup nor a sidebar!\x1B[0m\n");
|
|
127
|
+
try {
|
|
128
|
+
const optionsPage = await browser.newPage();
|
|
129
|
+
const optionsUrl = `chrome-extension://${extensionId}/options.html`;
|
|
130
|
+
const response = await optionsPage.goto(optionsUrl, {
|
|
131
|
+
waitUntil: "domcontentloaded",
|
|
132
|
+
timeout: 5e3
|
|
133
|
+
});
|
|
134
|
+
if (response && response.ok()) {
|
|
135
|
+
await optionsPage.waitForSelector(waitSelector);
|
|
136
|
+
await new Promise((r) => setTimeout(r, settleDelay));
|
|
137
|
+
captured.options = await optionsPage.screenshot({
|
|
138
|
+
type: "png",
|
|
139
|
+
fullPage: true
|
|
140
|
+
});
|
|
141
|
+
console.log(` ✅ options captured`);
|
|
142
|
+
}
|
|
143
|
+
} catch {
|
|
144
|
+
console.log(" ⚠ No options found");
|
|
145
|
+
}
|
|
146
|
+
return captured;
|
|
147
|
+
} catch (err) {
|
|
148
|
+
if (err?.message === "An `executablePath` or `channel` must be specified for `puppeteer-core`") throw new Error("Chromium not detected. Set the VITESHOT_CHROME_PATH env var to your Chromium executable.");
|
|
149
|
+
else throw err;
|
|
150
|
+
} finally {
|
|
151
|
+
await browser?.close().catch(() => {});
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Capture popup/sidebar/options screenshots for all locales (batch).
|
|
156
|
+
* Used by the `export` command.
|
|
157
|
+
*/
|
|
158
|
+
async function captureExtensionScreenshots(options) {
|
|
159
|
+
const cwd = process.cwd();
|
|
160
|
+
const extensionPath = resolve(cwd, options.extensionPath);
|
|
161
|
+
const locales = options.localesDir ? await getLocales(resolve(cwd, options.localesDir)) : [];
|
|
162
|
+
const languages = locales.length > 0 ? locales.map((l) => l.language) : ["_default"];
|
|
163
|
+
console.log(`\n\x1b[1mCapturing extension screenshots${locales.length > 0 ? ` for ${locales.length} locales` : ""}...\x1b[0m\n`);
|
|
164
|
+
const result = /* @__PURE__ */ new Map();
|
|
165
|
+
for (const language of languages) {
|
|
166
|
+
const captured = await captureLocale({
|
|
167
|
+
extensionPath,
|
|
168
|
+
chromePath: options.chromePath,
|
|
169
|
+
language,
|
|
170
|
+
waitSelector: options.waitSelector,
|
|
171
|
+
settleDelay: options.settleDelay
|
|
172
|
+
});
|
|
173
|
+
result.set(language, captured);
|
|
174
|
+
}
|
|
175
|
+
console.log("");
|
|
176
|
+
return result;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
//#endregion
|
|
180
|
+
export { logInvalidDesignFiles as i, captureLocale as n, getScreenshots as r, captureExtensionScreenshots as t };
|
package/dist/cli.mjs
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { styleText } from "node:util";
|
|
3
|
+
|
|
4
|
+
//#region src/cli.ts
|
|
5
|
+
const DEFAULT_DIR = "store";
|
|
6
|
+
const [command, ...args] = process.argv.slice(2);
|
|
7
|
+
const showHelp = process.argv.includes("--help") || process.argv.includes("-h");
|
|
8
|
+
function getFlag(name) {
|
|
9
|
+
const prefix = `--${name}=`;
|
|
10
|
+
return args.find((a) => a.startsWith(prefix))?.slice(prefix.length);
|
|
11
|
+
}
|
|
12
|
+
switch (command) {
|
|
13
|
+
case "dev": {
|
|
14
|
+
const { DEV_HELP_MESSAGE } = await import("./help-messages-B_-FA6OE.mjs");
|
|
15
|
+
if (showHelp) {
|
|
16
|
+
console.log(DEV_HELP_MESSAGE);
|
|
17
|
+
process.exit(0);
|
|
18
|
+
}
|
|
19
|
+
const { createServer } = await import("./create-server-DwNGwEEV.mjs");
|
|
20
|
+
const server = await createServer(args[0] ?? DEFAULT_DIR);
|
|
21
|
+
await server.listen();
|
|
22
|
+
server.printUrls();
|
|
23
|
+
break;
|
|
24
|
+
}
|
|
25
|
+
case "export": {
|
|
26
|
+
const { EXPORT_HELP_MESSAGE } = await import("./help-messages-B_-FA6OE.mjs");
|
|
27
|
+
if (showHelp) {
|
|
28
|
+
console.log(EXPORT_HELP_MESSAGE);
|
|
29
|
+
process.exit(0);
|
|
30
|
+
}
|
|
31
|
+
const dir = args[0] ?? DEFAULT_DIR;
|
|
32
|
+
const { resolveConfig } = await import("./config-Cs-Dm27M.mjs");
|
|
33
|
+
const config = await resolveConfig(dir);
|
|
34
|
+
config.chromePath = getFlag("chromePath") ?? config.chromePath;
|
|
35
|
+
const { exportScreenshots } = await import("./export-screenshots-B77VFVWT.mjs");
|
|
36
|
+
await exportScreenshots(config);
|
|
37
|
+
process.exit(0);
|
|
38
|
+
}
|
|
39
|
+
case "popup": {
|
|
40
|
+
const { POPUP_HELP_MESSAGE } = await import("./help-messages-B_-FA6OE.mjs");
|
|
41
|
+
if (showHelp) {
|
|
42
|
+
console.log(POPUP_HELP_MESSAGE);
|
|
43
|
+
process.exit(0);
|
|
44
|
+
}
|
|
45
|
+
const { exportPopupScreenshot } = await import("./export-popup-screenshot-Dp7G2Ylf.mjs");
|
|
46
|
+
await exportPopupScreenshot({
|
|
47
|
+
extensionPath: getFlag("extension-path") ?? ".output/chrome-mv3",
|
|
48
|
+
chromePath: getFlag("chromePath"),
|
|
49
|
+
outputDir: getFlag("output"),
|
|
50
|
+
waitSelector: getFlag("wait-selector"),
|
|
51
|
+
settleDelay: getFlag("settle-delay") ? Number(getFlag("settle-delay")) : void 0
|
|
52
|
+
});
|
|
53
|
+
process.exit(0);
|
|
54
|
+
}
|
|
55
|
+
case "init": {
|
|
56
|
+
const { INIT_HELP_MESSAGE } = await import("./help-messages-B_-FA6OE.mjs");
|
|
57
|
+
if (showHelp) {
|
|
58
|
+
console.log(INIT_HELP_MESSAGE);
|
|
59
|
+
process.exit(0);
|
|
60
|
+
}
|
|
61
|
+
const { init } = await import("./init-BUrgR61N.mjs");
|
|
62
|
+
await init(args[0] ?? DEFAULT_DIR);
|
|
63
|
+
process.exit(0);
|
|
64
|
+
}
|
|
65
|
+
default: {
|
|
66
|
+
const { GENERAL_HELP_MESSAGE } = await import("./help-messages-B_-FA6OE.mjs");
|
|
67
|
+
if (showHelp) {
|
|
68
|
+
console.log(GENERAL_HELP_MESSAGE);
|
|
69
|
+
process.exit(0);
|
|
70
|
+
}
|
|
71
|
+
console.log(`${GENERAL_HELP_MESSAGE}\n\nUnknown command: ${styleText("yellow", command || "<none>")}`);
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
//#endregion
|
|
77
|
+
export { };
|