frameshot-mcp 0.8.0 → 0.9.7
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/README.md +68 -122
- package/action.yml +14 -5
- package/dist/{chunk-PYWXJZTZ.js → chunk-MEBQ7ZWA.js} +760 -109
- package/dist/{chunk-MA3FOIQY.js → chunk-VUYZHZBH.js} +1 -1
- package/dist/cli.js +128 -115
- package/dist/index.js +377 -528
- package/dist/renderer.d.ts +7 -3
- package/dist/renderer.js +1 -1
- package/dist/stubs/gatsby.js +20 -0
- package/dist/stubs/next-font.js +9 -0
- package/dist/stubs/next-headers.js +20 -0
- package/dist/stubs/next-image.js +34 -0
- package/dist/stubs/next-link.js +25 -0
- package/dist/stubs/next-navigation.js +17 -0
- package/dist/stubs/next-router.js +19 -0
- package/dist/stubs/nuxt-app.js +37 -0
- package/dist/stubs/nuxt-imports.js +13 -0
- package/dist/stubs/qwik-city.js +33 -0
- package/dist/stubs/react-router.js +67 -0
- package/dist/stubs/server-only.js +2 -0
- package/dist/stubs/solid-router.js +27 -0
- package/dist/stubs/solid-start.js +18 -0
- package/dist/stubs/stubs/gatsby.js +20 -0
- package/dist/stubs/stubs/next-font.js +9 -0
- package/dist/stubs/stubs/next-headers.js +20 -0
- package/dist/stubs/stubs/next-image.js +34 -0
- package/dist/stubs/stubs/next-link.js +25 -0
- package/dist/stubs/stubs/next-navigation.js +17 -0
- package/dist/stubs/stubs/next-router.js +19 -0
- package/dist/stubs/stubs/nuxt-app.js +37 -0
- package/dist/stubs/stubs/nuxt-imports.js +13 -0
- package/dist/stubs/stubs/qwik-city.js +33 -0
- package/dist/stubs/stubs/react-router.js +67 -0
- package/dist/stubs/stubs/server-only.js +2 -0
- package/dist/stubs/stubs/solid-router.js +27 -0
- package/dist/stubs/stubs/solid-start.js +18 -0
- package/dist/stubs/stubs/sveltekit-environment.js +5 -0
- package/dist/stubs/stubs/sveltekit-navigation.js +11 -0
- package/dist/stubs/stubs/sveltekit-stores.js +15 -0
- package/dist/stubs/stubs/vike.js +11 -0
- package/dist/stubs/sveltekit-environment.js +5 -0
- package/dist/stubs/sveltekit-navigation.js +11 -0
- package/dist/stubs/sveltekit-stores.js +15 -0
- package/dist/stubs/vike.js +11 -0
- package/package.json +8 -3
- package/scripts/render-changed.mjs +67 -12
- package/dist/chunk-3CDSNOX5.js +0 -869
- package/dist/chunk-3LVWVDET.js +0 -849
- package/dist/chunk-47YJG5HR.js +0 -690
- package/dist/chunk-67JZQ6OI.js +0 -819
- package/dist/chunk-AUACBLHM.js +0 -191
- package/dist/chunk-AZCGKIMU.js +0 -850
- package/dist/chunk-B3CLIGWU.js +0 -786
- package/dist/chunk-C6QSY4WR.js +0 -811
- package/dist/chunk-DX54PJKO.js +0 -603
- package/dist/chunk-EMCJGIMY.js +0 -984
- package/dist/chunk-FQNWGR62.js +0 -849
- package/dist/chunk-FTYTZW6D.js +0 -203
- package/dist/chunk-GVOEFYEX.js +0 -139
- package/dist/chunk-JGVKYXY2.js +0 -857
- package/dist/chunk-JYPEA4P2.js +0 -846
- package/dist/chunk-KHK35HDD.js +0 -855
- package/dist/chunk-L2CADTS7.js +0 -191
- package/dist/chunk-O7NWWFIU.js +0 -871
- package/dist/chunk-Q7A3DLED.js +0 -848
- package/dist/chunk-Q7NQA4ZM.js +0 -1095
- package/dist/chunk-SIA6XEHM.js +0 -811
- package/dist/chunk-ST35YDI6.js +0 -834
- package/dist/chunk-T5OBJK35.js +0 -855
- package/dist/chunk-U3GHS7KO.js +0 -837
- package/dist/chunk-WS2ASCD6.js +0 -683
- package/dist/chunk-WZMHVSUA.js +0 -847
- package/dist/chunk-ZZST6K7Y.js +0 -987
|
@@ -4,6 +4,31 @@ var __export = (target, all) => {
|
|
|
4
4
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
5
5
|
};
|
|
6
6
|
|
|
7
|
+
// src/domain/types.ts
|
|
8
|
+
var DEFAULT_RENDER_OPTIONS = {
|
|
9
|
+
viewport: { width: 1280, height: 800 },
|
|
10
|
+
engines: ["chromium"],
|
|
11
|
+
fullPage: true,
|
|
12
|
+
darkMode: false,
|
|
13
|
+
css: "",
|
|
14
|
+
tailwindVersion: "3",
|
|
15
|
+
waitFor: 0,
|
|
16
|
+
autoFit: false
|
|
17
|
+
};
|
|
18
|
+
var DEVICE_PRESETS = {
|
|
19
|
+
mobile: { width: 375, height: 667 },
|
|
20
|
+
tablet: { width: 768, height: 1024 },
|
|
21
|
+
desktop: { width: 1280, height: 800 }
|
|
22
|
+
};
|
|
23
|
+
var EXT_TO_FRAMEWORK = {
|
|
24
|
+
".jsx": "react",
|
|
25
|
+
".tsx": "react",
|
|
26
|
+
".vue": "vue",
|
|
27
|
+
".svelte": "svelte",
|
|
28
|
+
".html": "html",
|
|
29
|
+
".htm": "html"
|
|
30
|
+
};
|
|
31
|
+
|
|
7
32
|
// src/infrastructure/browser-pool.ts
|
|
8
33
|
import { chromium, firefox, webkit } from "playwright";
|
|
9
34
|
var BrowserPool = class {
|
|
@@ -78,14 +103,18 @@ var BrowserPool = class {
|
|
|
78
103
|
const launcher = { chromium, firefox, webkit }[engine];
|
|
79
104
|
let browser;
|
|
80
105
|
try {
|
|
81
|
-
browser = await launcher.launch({
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
)
|
|
106
|
+
browser = await launcher.launch({ headless: true });
|
|
107
|
+
} catch {
|
|
108
|
+
try {
|
|
109
|
+
browser = await launcher.launch({
|
|
110
|
+
headless: true,
|
|
111
|
+
...engine === "chromium" ? { channel: "chrome" } : {}
|
|
112
|
+
});
|
|
113
|
+
} catch (_e) {
|
|
114
|
+
throw new Error(
|
|
115
|
+
`${engine} is not installed. Run: npx playwright install ${engine}`
|
|
116
|
+
);
|
|
117
|
+
}
|
|
89
118
|
}
|
|
90
119
|
const slot = { browser, idlePages: [] };
|
|
91
120
|
this.pool.set(engine, slot);
|
|
@@ -247,7 +276,9 @@ var ProjectDetector = class {
|
|
|
247
276
|
const viteConfigPath = this.findViteConfig(root);
|
|
248
277
|
const framework = this.detectFramework(root);
|
|
249
278
|
const hasVite = this.checkViteAvailable(root);
|
|
250
|
-
|
|
279
|
+
const isNextJs = this.checkIsNextJs(root);
|
|
280
|
+
const pathAliases = this.readTsconfigAliases(root);
|
|
281
|
+
return { root, viteConfigPath, framework, hasVite, isNextJs, pathAliases };
|
|
251
282
|
}
|
|
252
283
|
findProjectRoot(startPath) {
|
|
253
284
|
let dir = dirname(resolve(startPath));
|
|
@@ -277,6 +308,8 @@ var ProjectDetector = class {
|
|
|
277
308
|
...pkg.dependencies,
|
|
278
309
|
...pkg.devDependencies
|
|
279
310
|
};
|
|
311
|
+
if (allDeps["solid-js"]) return "solid";
|
|
312
|
+
if (allDeps.preact) return "preact";
|
|
280
313
|
if (allDeps.react || allDeps["react-dom"]) return "react";
|
|
281
314
|
if (allDeps.vue) return "vue";
|
|
282
315
|
if (allDeps.svelte) return "svelte";
|
|
@@ -288,19 +321,601 @@ var ProjectDetector = class {
|
|
|
288
321
|
const vitePkgPath = join(root, "node_modules", "vite", "package.json");
|
|
289
322
|
return existsSync(vitePkgPath);
|
|
290
323
|
}
|
|
324
|
+
checkIsNextJs(root) {
|
|
325
|
+
const pkgPath = join(root, "package.json");
|
|
326
|
+
if (!existsSync(pkgPath)) return false;
|
|
327
|
+
try {
|
|
328
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
329
|
+
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
330
|
+
return !!allDeps.next;
|
|
331
|
+
} catch {
|
|
332
|
+
return false;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
readTsconfigAliases(root) {
|
|
336
|
+
const tscPath = join(root, "tsconfig.json");
|
|
337
|
+
if (!existsSync(tscPath)) return {};
|
|
338
|
+
try {
|
|
339
|
+
const content = readFileSync(tscPath, "utf-8");
|
|
340
|
+
const stripped = content.replace(/^\s*\/\/[^\n]*/gm, "");
|
|
341
|
+
const tsc = JSON.parse(stripped);
|
|
342
|
+
const paths = tsc.compilerOptions?.paths ?? {};
|
|
343
|
+
const baseUrl = tsc.compilerOptions?.baseUrl ?? ".";
|
|
344
|
+
const result = {};
|
|
345
|
+
for (const [alias, targets] of Object.entries(paths)) {
|
|
346
|
+
const key = alias.replace(/\/\*$/, "");
|
|
347
|
+
const target = (targets[0] ?? "").replace(/\/\*$/, "");
|
|
348
|
+
result[key] = join(root, baseUrl, target);
|
|
349
|
+
}
|
|
350
|
+
return result;
|
|
351
|
+
} catch {
|
|
352
|
+
return {};
|
|
353
|
+
}
|
|
354
|
+
}
|
|
291
355
|
};
|
|
292
356
|
|
|
293
357
|
// src/infrastructure/vite-bundler.ts
|
|
294
|
-
import { existsSync as
|
|
358
|
+
import { existsSync as existsSync4 } from "fs";
|
|
295
359
|
import { createRequire } from "module";
|
|
296
|
-
import { join as
|
|
360
|
+
import { dirname as dirname2, join as join18, resolve as resolve2 } from "path";
|
|
361
|
+
import { fileURLToPath } from "url";
|
|
362
|
+
|
|
363
|
+
// src/infrastructure/adapters/astro.ts
|
|
364
|
+
import { join as join3 } from "path";
|
|
365
|
+
|
|
366
|
+
// src/infrastructure/adapters/helpers.ts
|
|
367
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
368
|
+
import { join as join2 } from "path";
|
|
369
|
+
function hasDep(pkgPath, names) {
|
|
370
|
+
if (!existsSync2(pkgPath)) return false;
|
|
371
|
+
try {
|
|
372
|
+
const pkg = JSON.parse(readFileSync2(pkgPath, "utf-8"));
|
|
373
|
+
const all = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
374
|
+
return names.some((n) => !!all[n]);
|
|
375
|
+
} catch {
|
|
376
|
+
return false;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
function hasInstalled(projectRoot, name) {
|
|
380
|
+
return existsSync2(join2(projectRoot, "node_modules", ...name.split("/")));
|
|
381
|
+
}
|
|
382
|
+
async function loadPlugin(pkg, fnName) {
|
|
383
|
+
try {
|
|
384
|
+
const mod = await import(pkg);
|
|
385
|
+
const candidate = fnName ? mod[fnName] : mod.default ?? mod;
|
|
386
|
+
if (typeof candidate === "function") {
|
|
387
|
+
const result = candidate();
|
|
388
|
+
return Array.isArray(result) ? result : [result];
|
|
389
|
+
}
|
|
390
|
+
} catch {
|
|
391
|
+
process.stderr.write(`[frameshot] ${pkg} not available
|
|
392
|
+
`);
|
|
393
|
+
}
|
|
394
|
+
return [];
|
|
395
|
+
}
|
|
396
|
+
function loadReactPlugin() {
|
|
397
|
+
return loadPlugin("@vitejs/plugin-react");
|
|
398
|
+
}
|
|
399
|
+
function rscDirectivePlugin() {
|
|
400
|
+
return {
|
|
401
|
+
name: "frameshot-rsc-directives",
|
|
402
|
+
transform(code) {
|
|
403
|
+
return code.replace(/^['"]use (client|server)['"];?\s*/m, "");
|
|
404
|
+
}
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// src/infrastructure/adapters/astro.ts
|
|
409
|
+
var AstroAdapter = class {
|
|
410
|
+
id = "astro";
|
|
411
|
+
detect(projectRoot) {
|
|
412
|
+
return hasDep(join3(projectRoot, "package.json"), ["astro"]);
|
|
413
|
+
}
|
|
414
|
+
getAliases() {
|
|
415
|
+
return {};
|
|
416
|
+
}
|
|
417
|
+
getOptimizeDeps() {
|
|
418
|
+
return [];
|
|
419
|
+
}
|
|
420
|
+
async getPlugins(projectRoot) {
|
|
421
|
+
const plugins = [];
|
|
422
|
+
if (hasInstalled(projectRoot, "react"))
|
|
423
|
+
plugins.push(...await loadReactPlugin());
|
|
424
|
+
if (hasInstalled(projectRoot, "vue"))
|
|
425
|
+
plugins.push(...await loadPlugin("@vitejs/plugin-vue"));
|
|
426
|
+
if (hasInstalled(projectRoot, "svelte"))
|
|
427
|
+
plugins.push(
|
|
428
|
+
...await loadPlugin("@sveltejs/vite-plugin-svelte", "svelte")
|
|
429
|
+
);
|
|
430
|
+
if (hasInstalled(projectRoot, "solid-js"))
|
|
431
|
+
plugins.push(...await loadPlugin("vite-plugin-solid"));
|
|
432
|
+
return plugins;
|
|
433
|
+
}
|
|
434
|
+
useFrameshotVite() {
|
|
435
|
+
return true;
|
|
436
|
+
}
|
|
437
|
+
skipProjectConfig() {
|
|
438
|
+
return true;
|
|
439
|
+
}
|
|
440
|
+
};
|
|
441
|
+
|
|
442
|
+
// src/infrastructure/adapters/gatsby.ts
|
|
443
|
+
import { join as join4 } from "path";
|
|
444
|
+
var GatsbyAdapter = class {
|
|
445
|
+
id = "gatsby";
|
|
446
|
+
detect(projectRoot) {
|
|
447
|
+
return hasDep(join4(projectRoot, "package.json"), ["gatsby"]);
|
|
448
|
+
}
|
|
449
|
+
getAliases(_root, stubsDir) {
|
|
450
|
+
return { gatsby: join4(stubsDir, "gatsby.js") };
|
|
451
|
+
}
|
|
452
|
+
getOptimizeDeps(_root) {
|
|
453
|
+
return ["react", "react-dom/client"];
|
|
454
|
+
}
|
|
455
|
+
async getPlugins(_root) {
|
|
456
|
+
return loadReactPlugin();
|
|
457
|
+
}
|
|
458
|
+
useFrameshotVite() {
|
|
459
|
+
return true;
|
|
460
|
+
}
|
|
461
|
+
skipProjectConfig() {
|
|
462
|
+
return true;
|
|
463
|
+
}
|
|
464
|
+
};
|
|
465
|
+
|
|
466
|
+
// src/infrastructure/adapters/generic.ts
|
|
467
|
+
var GenericAdapter = class {
|
|
468
|
+
id = "generic";
|
|
469
|
+
detect() {
|
|
470
|
+
return true;
|
|
471
|
+
}
|
|
472
|
+
getAliases() {
|
|
473
|
+
return {};
|
|
474
|
+
}
|
|
475
|
+
getOptimizeDeps(projectRoot) {
|
|
476
|
+
const deps = [];
|
|
477
|
+
if (hasInstalled(projectRoot, "react"))
|
|
478
|
+
deps.push("react", "react-dom/client");
|
|
479
|
+
if (hasInstalled(projectRoot, "vue")) deps.push("vue");
|
|
480
|
+
if (hasInstalled(projectRoot, "solid-js"))
|
|
481
|
+
deps.push("solid-js", "solid-js/web");
|
|
482
|
+
if (hasInstalled(projectRoot, "preact")) deps.push("preact");
|
|
483
|
+
return deps;
|
|
484
|
+
}
|
|
485
|
+
async getPlugins(projectRoot) {
|
|
486
|
+
const plugins = [];
|
|
487
|
+
if (hasInstalled(projectRoot, "react"))
|
|
488
|
+
plugins.push(...await loadReactPlugin());
|
|
489
|
+
if (hasInstalled(projectRoot, "vue"))
|
|
490
|
+
plugins.push(...await loadPlugin("@vitejs/plugin-vue"));
|
|
491
|
+
if (hasInstalled(projectRoot, "svelte"))
|
|
492
|
+
plugins.push(
|
|
493
|
+
...await loadPlugin("@sveltejs/vite-plugin-svelte", "svelte")
|
|
494
|
+
);
|
|
495
|
+
if (hasInstalled(projectRoot, "solid-js"))
|
|
496
|
+
plugins.push(...await loadPlugin("vite-plugin-solid"));
|
|
497
|
+
if (hasInstalled(projectRoot, "preact"))
|
|
498
|
+
plugins.push(...await loadPlugin("@preact/preset-vite"));
|
|
499
|
+
return plugins;
|
|
500
|
+
}
|
|
501
|
+
useFrameshotVite() {
|
|
502
|
+
return true;
|
|
503
|
+
}
|
|
504
|
+
skipProjectConfig() {
|
|
505
|
+
return true;
|
|
506
|
+
}
|
|
507
|
+
};
|
|
508
|
+
|
|
509
|
+
// src/infrastructure/adapters/lit.ts
|
|
510
|
+
import { join as join5 } from "path";
|
|
511
|
+
var LitAdapter = class {
|
|
512
|
+
id = "lit";
|
|
513
|
+
detect(projectRoot) {
|
|
514
|
+
return hasDep(join5(projectRoot, "package.json"), ["lit", "lit-element"]);
|
|
515
|
+
}
|
|
516
|
+
getAliases() {
|
|
517
|
+
return {};
|
|
518
|
+
}
|
|
519
|
+
getOptimizeDeps() {
|
|
520
|
+
return ["lit"];
|
|
521
|
+
}
|
|
522
|
+
async getPlugins(_root) {
|
|
523
|
+
return [];
|
|
524
|
+
}
|
|
525
|
+
useFrameshotVite() {
|
|
526
|
+
return true;
|
|
527
|
+
}
|
|
528
|
+
skipProjectConfig() {
|
|
529
|
+
return true;
|
|
530
|
+
}
|
|
531
|
+
};
|
|
532
|
+
|
|
533
|
+
// src/infrastructure/adapters/next.ts
|
|
534
|
+
import { join as join6 } from "path";
|
|
535
|
+
var NextJsAdapter = class {
|
|
536
|
+
id = "next";
|
|
537
|
+
detect(projectRoot) {
|
|
538
|
+
return hasDep(join6(projectRoot, "package.json"), ["next"]);
|
|
539
|
+
}
|
|
540
|
+
getAliases(_root, stubsDir) {
|
|
541
|
+
return {
|
|
542
|
+
"next/navigation": join6(stubsDir, "next-navigation.js"),
|
|
543
|
+
"next/router": join6(stubsDir, "next-router.js"),
|
|
544
|
+
"next/image": join6(stubsDir, "next-image.js"),
|
|
545
|
+
"next/link": join6(stubsDir, "next-link.js"),
|
|
546
|
+
"next/font/google": join6(stubsDir, "next-font.js"),
|
|
547
|
+
"next/font/local": join6(stubsDir, "next-font.js"),
|
|
548
|
+
"next/headers": join6(stubsDir, "next-headers.js"),
|
|
549
|
+
"server-only": join6(stubsDir, "server-only.js"),
|
|
550
|
+
"client-only": join6(stubsDir, "server-only.js")
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
getOptimizeDeps(projectRoot) {
|
|
554
|
+
const base = ["react", "react-dom/client"];
|
|
555
|
+
const optional = [
|
|
556
|
+
"@mui/material",
|
|
557
|
+
"@mui/icons-material",
|
|
558
|
+
"@emotion/react",
|
|
559
|
+
"@emotion/styled",
|
|
560
|
+
"@chakra-ui/react",
|
|
561
|
+
"styled-components"
|
|
562
|
+
];
|
|
563
|
+
return [
|
|
564
|
+
...base,
|
|
565
|
+
...optional.filter((p) => hasInstalled(projectRoot, p.split("/")[0]))
|
|
566
|
+
];
|
|
567
|
+
}
|
|
568
|
+
async getPlugins(_root) {
|
|
569
|
+
return [...await loadReactPlugin(), rscDirectivePlugin()];
|
|
570
|
+
}
|
|
571
|
+
useFrameshotVite() {
|
|
572
|
+
return true;
|
|
573
|
+
}
|
|
574
|
+
skipProjectConfig() {
|
|
575
|
+
return true;
|
|
576
|
+
}
|
|
577
|
+
};
|
|
578
|
+
|
|
579
|
+
// src/infrastructure/adapters/nuxt.ts
|
|
580
|
+
import { join as join7 } from "path";
|
|
581
|
+
var NuxtAdapter = class {
|
|
582
|
+
id = "nuxt";
|
|
583
|
+
detect(projectRoot) {
|
|
584
|
+
return hasDep(join7(projectRoot, "package.json"), ["nuxt"]);
|
|
585
|
+
}
|
|
586
|
+
getAliases(_root, stubsDir) {
|
|
587
|
+
return {
|
|
588
|
+
"#app": join7(stubsDir, "nuxt-app.js"),
|
|
589
|
+
"#imports": join7(stubsDir, "nuxt-imports.js"),
|
|
590
|
+
"#head": join7(stubsDir, "nuxt-app.js")
|
|
591
|
+
};
|
|
592
|
+
}
|
|
593
|
+
getOptimizeDeps(_root) {
|
|
594
|
+
return ["vue"];
|
|
595
|
+
}
|
|
596
|
+
async getPlugins(_root) {
|
|
597
|
+
return loadPlugin("@vitejs/plugin-vue");
|
|
598
|
+
}
|
|
599
|
+
useFrameshotVite() {
|
|
600
|
+
return true;
|
|
601
|
+
}
|
|
602
|
+
skipProjectConfig() {
|
|
603
|
+
return true;
|
|
604
|
+
}
|
|
605
|
+
};
|
|
606
|
+
|
|
607
|
+
// src/infrastructure/adapters/preact.ts
|
|
608
|
+
import { join as join8 } from "path";
|
|
609
|
+
var PreactAdapter = class {
|
|
610
|
+
id = "preact";
|
|
611
|
+
detect(projectRoot) {
|
|
612
|
+
return hasDep(join8(projectRoot, "package.json"), ["preact"]);
|
|
613
|
+
}
|
|
614
|
+
getAliases() {
|
|
615
|
+
return {};
|
|
616
|
+
}
|
|
617
|
+
getOptimizeDeps() {
|
|
618
|
+
return ["preact", "preact/hooks"];
|
|
619
|
+
}
|
|
620
|
+
async getPlugins(_root) {
|
|
621
|
+
return loadPlugin("@preact/preset-vite");
|
|
622
|
+
}
|
|
623
|
+
useFrameshotVite() {
|
|
624
|
+
return true;
|
|
625
|
+
}
|
|
626
|
+
skipProjectConfig() {
|
|
627
|
+
return true;
|
|
628
|
+
}
|
|
629
|
+
};
|
|
630
|
+
|
|
631
|
+
// src/infrastructure/adapters/qwik.ts
|
|
632
|
+
import { join as join9 } from "path";
|
|
633
|
+
var QwikAdapter = class {
|
|
634
|
+
id = "qwik";
|
|
635
|
+
detect(projectRoot) {
|
|
636
|
+
return hasDep(join9(projectRoot, "package.json"), ["@builder.io/qwik"]);
|
|
637
|
+
}
|
|
638
|
+
getAliases(_root, stubsDir) {
|
|
639
|
+
return { "@builder.io/qwik-city": join9(stubsDir, "qwik-city.js") };
|
|
640
|
+
}
|
|
641
|
+
getOptimizeDeps(_root) {
|
|
642
|
+
return ["@builder.io/qwik"];
|
|
643
|
+
}
|
|
644
|
+
async getPlugins(_root) {
|
|
645
|
+
return loadPlugin("@builder.io/qwik/optimizer", "qwikVite");
|
|
646
|
+
}
|
|
647
|
+
useFrameshotVite() {
|
|
648
|
+
return false;
|
|
649
|
+
}
|
|
650
|
+
skipProjectConfig() {
|
|
651
|
+
return true;
|
|
652
|
+
}
|
|
653
|
+
};
|
|
654
|
+
|
|
655
|
+
// src/infrastructure/adapters/react-router.ts
|
|
656
|
+
import { join as join10 } from "path";
|
|
657
|
+
var ReactRouterAdapter = class {
|
|
658
|
+
id = "react-router";
|
|
659
|
+
detect(projectRoot) {
|
|
660
|
+
return hasDep(join10(projectRoot, "package.json"), [
|
|
661
|
+
"@react-router/dev",
|
|
662
|
+
"@remix-run/react"
|
|
663
|
+
]);
|
|
664
|
+
}
|
|
665
|
+
getAliases(_root, stubsDir) {
|
|
666
|
+
return {
|
|
667
|
+
"react-router": join10(stubsDir, "react-router.js"),
|
|
668
|
+
"react-router-dom": join10(stubsDir, "react-router.js"),
|
|
669
|
+
"@remix-run/react": join10(stubsDir, "react-router.js")
|
|
670
|
+
};
|
|
671
|
+
}
|
|
672
|
+
getOptimizeDeps(_root) {
|
|
673
|
+
return ["react", "react-dom/client"];
|
|
674
|
+
}
|
|
675
|
+
async getPlugins(_root) {
|
|
676
|
+
return [...await loadReactPlugin(), rscDirectivePlugin()];
|
|
677
|
+
}
|
|
678
|
+
useFrameshotVite() {
|
|
679
|
+
return true;
|
|
680
|
+
}
|
|
681
|
+
skipProjectConfig() {
|
|
682
|
+
return true;
|
|
683
|
+
}
|
|
684
|
+
};
|
|
685
|
+
|
|
686
|
+
// src/infrastructure/adapters/solid.ts
|
|
687
|
+
import { join as join11 } from "path";
|
|
688
|
+
var SolidAdapter = class {
|
|
689
|
+
id = "solid";
|
|
690
|
+
detect(projectRoot) {
|
|
691
|
+
return hasDep(join11(projectRoot, "package.json"), ["solid-js"]);
|
|
692
|
+
}
|
|
693
|
+
getAliases(_root, stubsDir) {
|
|
694
|
+
return { "@solidjs/router": join11(stubsDir, "solid-router.js") };
|
|
695
|
+
}
|
|
696
|
+
getOptimizeDeps() {
|
|
697
|
+
return ["solid-js", "solid-js/web"];
|
|
698
|
+
}
|
|
699
|
+
async getPlugins(_root) {
|
|
700
|
+
return loadPlugin("vite-plugin-solid");
|
|
701
|
+
}
|
|
702
|
+
useFrameshotVite() {
|
|
703
|
+
return true;
|
|
704
|
+
}
|
|
705
|
+
skipProjectConfig() {
|
|
706
|
+
return true;
|
|
707
|
+
}
|
|
708
|
+
};
|
|
709
|
+
|
|
710
|
+
// src/infrastructure/adapters/solid-start.ts
|
|
711
|
+
import { join as join12 } from "path";
|
|
712
|
+
var SolidStartAdapter = class {
|
|
713
|
+
id = "solid-start";
|
|
714
|
+
detect(projectRoot) {
|
|
715
|
+
return hasDep(join12(projectRoot, "package.json"), ["@solidjs/start"]);
|
|
716
|
+
}
|
|
717
|
+
getAliases(_root, stubsDir) {
|
|
718
|
+
return {
|
|
719
|
+
"@solidjs/start": join12(stubsDir, "solid-start.js"),
|
|
720
|
+
"@solidjs/start/router": join12(stubsDir, "solid-router.js"),
|
|
721
|
+
"@solidjs/router": join12(stubsDir, "solid-router.js")
|
|
722
|
+
};
|
|
723
|
+
}
|
|
724
|
+
getOptimizeDeps(_root) {
|
|
725
|
+
return ["solid-js", "solid-js/web"];
|
|
726
|
+
}
|
|
727
|
+
async getPlugins(_root) {
|
|
728
|
+
return loadPlugin("vite-plugin-solid");
|
|
729
|
+
}
|
|
730
|
+
useFrameshotVite() {
|
|
731
|
+
return true;
|
|
732
|
+
}
|
|
733
|
+
skipProjectConfig() {
|
|
734
|
+
return true;
|
|
735
|
+
}
|
|
736
|
+
};
|
|
737
|
+
|
|
738
|
+
// src/infrastructure/adapters/svelte.ts
|
|
739
|
+
import { join as join13 } from "path";
|
|
740
|
+
var SvelteAdapter = class {
|
|
741
|
+
id = "svelte";
|
|
742
|
+
detect(projectRoot) {
|
|
743
|
+
return hasDep(join13(projectRoot, "package.json"), ["svelte"]);
|
|
744
|
+
}
|
|
745
|
+
getAliases() {
|
|
746
|
+
return {};
|
|
747
|
+
}
|
|
748
|
+
getOptimizeDeps() {
|
|
749
|
+
return [];
|
|
750
|
+
}
|
|
751
|
+
async getPlugins(_root) {
|
|
752
|
+
return loadPlugin("@sveltejs/vite-plugin-svelte", "svelte");
|
|
753
|
+
}
|
|
754
|
+
useFrameshotVite() {
|
|
755
|
+
return true;
|
|
756
|
+
}
|
|
757
|
+
skipProjectConfig() {
|
|
758
|
+
return true;
|
|
759
|
+
}
|
|
760
|
+
};
|
|
761
|
+
|
|
762
|
+
// src/infrastructure/adapters/sveltekit.ts
|
|
763
|
+
import { join as join14 } from "path";
|
|
764
|
+
var SvelteKitAdapter = class {
|
|
765
|
+
id = "sveltekit";
|
|
766
|
+
detect(projectRoot) {
|
|
767
|
+
return hasDep(join14(projectRoot, "package.json"), ["@sveltejs/kit"]);
|
|
768
|
+
}
|
|
769
|
+
getAliases(_root, stubsDir) {
|
|
770
|
+
return {
|
|
771
|
+
"$app/navigation": join14(stubsDir, "sveltekit-navigation.js"),
|
|
772
|
+
"$app/stores": join14(stubsDir, "sveltekit-stores.js"),
|
|
773
|
+
"$app/environment": join14(stubsDir, "sveltekit-environment.js")
|
|
774
|
+
};
|
|
775
|
+
}
|
|
776
|
+
getOptimizeDeps(_root) {
|
|
777
|
+
return [];
|
|
778
|
+
}
|
|
779
|
+
async getPlugins(_root) {
|
|
780
|
+
return loadPlugin("@sveltejs/vite-plugin-svelte", "svelte");
|
|
781
|
+
}
|
|
782
|
+
useFrameshotVite() {
|
|
783
|
+
return true;
|
|
784
|
+
}
|
|
785
|
+
skipProjectConfig() {
|
|
786
|
+
return true;
|
|
787
|
+
}
|
|
788
|
+
};
|
|
789
|
+
|
|
790
|
+
// src/infrastructure/adapters/vike.ts
|
|
791
|
+
import { join as join15 } from "path";
|
|
792
|
+
var VikeAdapter = class {
|
|
793
|
+
id = "vike";
|
|
794
|
+
detect(projectRoot) {
|
|
795
|
+
return hasDep(join15(projectRoot, "package.json"), ["vike"]);
|
|
796
|
+
}
|
|
797
|
+
getAliases(_root, stubsDir) {
|
|
798
|
+
return {
|
|
799
|
+
vike: join15(stubsDir, "vike.js"),
|
|
800
|
+
"vike/client/router": join15(stubsDir, "vike.js")
|
|
801
|
+
};
|
|
802
|
+
}
|
|
803
|
+
getOptimizeDeps(projectRoot) {
|
|
804
|
+
const deps = [];
|
|
805
|
+
if (hasInstalled(projectRoot, "react"))
|
|
806
|
+
deps.push("react", "react-dom/client");
|
|
807
|
+
if (hasInstalled(projectRoot, "vue")) deps.push("vue");
|
|
808
|
+
return deps;
|
|
809
|
+
}
|
|
810
|
+
async getPlugins(projectRoot) {
|
|
811
|
+
const plugins = [];
|
|
812
|
+
if (hasInstalled(projectRoot, "react"))
|
|
813
|
+
plugins.push(...await loadReactPlugin());
|
|
814
|
+
if (hasInstalled(projectRoot, "vue"))
|
|
815
|
+
plugins.push(...await loadPlugin("@vitejs/plugin-vue"));
|
|
816
|
+
return plugins;
|
|
817
|
+
}
|
|
818
|
+
useFrameshotVite() {
|
|
819
|
+
return true;
|
|
820
|
+
}
|
|
821
|
+
skipProjectConfig() {
|
|
822
|
+
return true;
|
|
823
|
+
}
|
|
824
|
+
};
|
|
825
|
+
|
|
826
|
+
// src/infrastructure/adapters/vite-react.ts
|
|
827
|
+
import { existsSync as existsSync3 } from "fs";
|
|
828
|
+
import { join as join16 } from "path";
|
|
829
|
+
var ViteReactAdapter = class {
|
|
830
|
+
id = "vite-react";
|
|
831
|
+
detect(projectRoot) {
|
|
832
|
+
return hasDep(join16(projectRoot, "package.json"), ["react"]) && this.hasViteConfig(projectRoot);
|
|
833
|
+
}
|
|
834
|
+
hasViteConfig(root) {
|
|
835
|
+
return ["vite.config.ts", "vite.config.js", "vite.config.mjs"].some(
|
|
836
|
+
(n) => existsSync3(join16(root, n))
|
|
837
|
+
);
|
|
838
|
+
}
|
|
839
|
+
getAliases() {
|
|
840
|
+
return {};
|
|
841
|
+
}
|
|
842
|
+
getOptimizeDeps() {
|
|
843
|
+
return ["react", "react-dom/client"];
|
|
844
|
+
}
|
|
845
|
+
async getPlugins() {
|
|
846
|
+
return [];
|
|
847
|
+
}
|
|
848
|
+
useFrameshotVite() {
|
|
849
|
+
return false;
|
|
850
|
+
}
|
|
851
|
+
skipProjectConfig() {
|
|
852
|
+
return false;
|
|
853
|
+
}
|
|
854
|
+
};
|
|
855
|
+
|
|
856
|
+
// src/infrastructure/adapters/vue.ts
|
|
857
|
+
import { join as join17 } from "path";
|
|
858
|
+
var VueAdapter = class {
|
|
859
|
+
id = "vue";
|
|
860
|
+
detect(projectRoot) {
|
|
861
|
+
return hasDep(join17(projectRoot, "package.json"), ["vue"]);
|
|
862
|
+
}
|
|
863
|
+
getAliases() {
|
|
864
|
+
return {};
|
|
865
|
+
}
|
|
866
|
+
getOptimizeDeps() {
|
|
867
|
+
return ["vue"];
|
|
868
|
+
}
|
|
869
|
+
async getPlugins(_root) {
|
|
870
|
+
return loadPlugin("@vitejs/plugin-vue");
|
|
871
|
+
}
|
|
872
|
+
useFrameshotVite() {
|
|
873
|
+
return true;
|
|
874
|
+
}
|
|
875
|
+
skipProjectConfig() {
|
|
876
|
+
return true;
|
|
877
|
+
}
|
|
878
|
+
};
|
|
879
|
+
|
|
880
|
+
// src/infrastructure/adapters/registry.ts
|
|
881
|
+
var ADAPTERS = [
|
|
882
|
+
// Meta-frameworks first — most specific match wins.
|
|
883
|
+
new NextJsAdapter(),
|
|
884
|
+
new NuxtAdapter(),
|
|
885
|
+
new SvelteKitAdapter(),
|
|
886
|
+
new SolidStartAdapter(),
|
|
887
|
+
new ReactRouterAdapter(),
|
|
888
|
+
new GatsbyAdapter(),
|
|
889
|
+
new QwikAdapter(),
|
|
890
|
+
new AstroAdapter(),
|
|
891
|
+
new VikeAdapter(),
|
|
892
|
+
// Core UI libraries.
|
|
893
|
+
new VueAdapter(),
|
|
894
|
+
new SvelteAdapter(),
|
|
895
|
+
new SolidAdapter(),
|
|
896
|
+
new PreactAdapter(),
|
|
897
|
+
new LitAdapter(),
|
|
898
|
+
new ViteReactAdapter(),
|
|
899
|
+
// Catch-all.
|
|
900
|
+
new GenericAdapter()
|
|
901
|
+
];
|
|
902
|
+
function selectAdapter(projectRoot) {
|
|
903
|
+
for (const adapter of ADAPTERS) {
|
|
904
|
+
if (adapter.detect(projectRoot)) return adapter;
|
|
905
|
+
}
|
|
906
|
+
return new GenericAdapter();
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
// src/infrastructure/vite-bundler.ts
|
|
910
|
+
var STUBS_DIR = join18(dirname2(fileURLToPath(import.meta.url)), "stubs");
|
|
297
911
|
var ViteBundler = class {
|
|
298
912
|
servers = /* @__PURE__ */ new Map();
|
|
299
913
|
detector = new ProjectDetector();
|
|
300
914
|
async getUrl(filePath, options = {}) {
|
|
301
915
|
const absPath = resolve2(filePath);
|
|
302
|
-
const project = options.projectRoot ? this.detector.detect(
|
|
303
|
-
|
|
916
|
+
const project = options.projectRoot ? this.detector.detect(join18(options.projectRoot, "package.json")) : this.detector.detect(absPath);
|
|
917
|
+
const adapter = selectAdapter(project.root);
|
|
918
|
+
if (!project.hasVite && !adapter.useFrameshotVite()) {
|
|
304
919
|
throw new Error(
|
|
305
920
|
`Vite not found in ${project.root}. Install vite: npm install -D vite`
|
|
306
921
|
);
|
|
@@ -312,7 +927,7 @@ var ViteBundler = class {
|
|
|
312
927
|
options.props,
|
|
313
928
|
project.root
|
|
314
929
|
);
|
|
315
|
-
const cached = await this.ensureServer(project);
|
|
930
|
+
const cached = await this.ensureServer(project, adapter);
|
|
316
931
|
cached.currentEntry = entryCode;
|
|
317
932
|
const mod = cached.server.moduleGraph.getModuleById(
|
|
318
933
|
"\0virtual:frameshot-entry"
|
|
@@ -332,17 +947,94 @@ var ViteBundler = class {
|
|
|
332
947
|
}
|
|
333
948
|
this.servers.clear();
|
|
334
949
|
}
|
|
335
|
-
|
|
950
|
+
// Public for testing
|
|
951
|
+
generateEntry(componentPath, framework, props, projectRoot) {
|
|
952
|
+
const propsJson = props ? JSON.stringify(props) : "{}";
|
|
953
|
+
const cssImport = projectRoot ? this.findGlobalCss(projectRoot) : "";
|
|
954
|
+
switch (framework) {
|
|
955
|
+
case "react": {
|
|
956
|
+
const hasRouter = projectRoot && this.hasPackage(projectRoot, "react-router-dom");
|
|
957
|
+
const routerImport = hasRouter ? `import { MemoryRouter } from "react-router-dom";` : "";
|
|
958
|
+
const wrap = hasRouter ? (inner) => `createElement(MemoryRouter, null, ${inner})` : (inner) => inner;
|
|
959
|
+
return `${cssImport}
|
|
960
|
+
import { createElement, Suspense } from "react";
|
|
961
|
+
import { createRoot } from "react-dom/client";
|
|
962
|
+
${routerImport}
|
|
963
|
+
import Component from "${componentPath}";
|
|
964
|
+
const props = ${propsJson};
|
|
965
|
+
const root = createRoot(document.getElementById("app"));
|
|
966
|
+
root.render(${wrap(`createElement(Suspense, { fallback: createElement("div", null, "Loading...") }, createElement(Component, props))`)});
|
|
967
|
+
`;
|
|
968
|
+
}
|
|
969
|
+
case "vue":
|
|
970
|
+
return `${cssImport}
|
|
971
|
+
import { createApp } from "vue";
|
|
972
|
+
import Component from "${componentPath}";
|
|
973
|
+
const props = ${propsJson};
|
|
974
|
+
createApp(Component, props).mount("#app");
|
|
975
|
+
`;
|
|
976
|
+
case "svelte":
|
|
977
|
+
return `${cssImport}
|
|
978
|
+
import Component from "${componentPath}";
|
|
979
|
+
const props = ${propsJson};
|
|
980
|
+
const target = document.getElementById("app");
|
|
981
|
+
// Svelte 5 uses mount(); Svelte 4 uses new Component(). Try v5 first.
|
|
982
|
+
try {
|
|
983
|
+
const svelte = await import("svelte");
|
|
984
|
+
if (typeof svelte.mount === "function") {
|
|
985
|
+
svelte.mount(Component, { target, props });
|
|
986
|
+
} else {
|
|
987
|
+
new Component({ target, props });
|
|
988
|
+
}
|
|
989
|
+
} catch {
|
|
990
|
+
new Component({ target, props });
|
|
991
|
+
}
|
|
992
|
+
`;
|
|
993
|
+
case "solid":
|
|
994
|
+
return `${cssImport}
|
|
995
|
+
import { render } from "solid-js/web";
|
|
996
|
+
import Component from "${componentPath}";
|
|
997
|
+
const props = ${propsJson};
|
|
998
|
+
render(() => Component(props), document.getElementById("app"));
|
|
999
|
+
`;
|
|
1000
|
+
case "preact":
|
|
1001
|
+
return `${cssImport}
|
|
1002
|
+
import { h, render } from "preact";
|
|
1003
|
+
import Component from "${componentPath}";
|
|
1004
|
+
const props = ${propsJson};
|
|
1005
|
+
render(h(Component, props), document.getElementById("app"));
|
|
1006
|
+
`;
|
|
1007
|
+
default:
|
|
1008
|
+
return `${cssImport}
|
|
1009
|
+
const res = await fetch("${componentPath}");
|
|
1010
|
+
const html = await res.text();
|
|
1011
|
+
document.getElementById("app").innerHTML = html;
|
|
1012
|
+
`;
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
async ensureServer(project, adapter) {
|
|
336
1016
|
const existing = this.servers.get(project.root);
|
|
337
1017
|
if (existing) return existing;
|
|
338
|
-
const vite = await this.importVite(project.root);
|
|
1018
|
+
const vite = adapter.useFrameshotVite() ? await this.importFrameshotVite() : await this.importVite(project.root);
|
|
339
1019
|
if (!vite) {
|
|
340
|
-
throw new Error(`Failed to import vite
|
|
1020
|
+
throw new Error(`Failed to import vite for ${project.root}`);
|
|
1021
|
+
}
|
|
1022
|
+
const adapterPlugins = await adapter.getPlugins(project.root);
|
|
1023
|
+
const adapterAliases = adapter.getAliases(project.root, STUBS_DIR);
|
|
1024
|
+
const aliasEntries = [];
|
|
1025
|
+
for (const [key, value] of Object.entries(project.pathAliases)) {
|
|
1026
|
+
aliasEntries.push({
|
|
1027
|
+
find: new RegExp(`^${key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}/`),
|
|
1028
|
+
replacement: `${value}/`
|
|
1029
|
+
});
|
|
1030
|
+
}
|
|
1031
|
+
for (const [key, value] of Object.entries(adapterAliases)) {
|
|
1032
|
+
aliasEntries.push({ find: key, replacement: value });
|
|
341
1033
|
}
|
|
342
1034
|
const self = this;
|
|
343
1035
|
const server = await vite.createServer({
|
|
344
1036
|
root: project.root,
|
|
345
|
-
configFile: project.viteConfigPath ?? false,
|
|
1037
|
+
configFile: adapter.skipProjectConfig() ? false : project.viteConfigPath ?? false,
|
|
346
1038
|
server: {
|
|
347
1039
|
port: 0,
|
|
348
1040
|
strictPort: false,
|
|
@@ -350,6 +1042,10 @@ var ViteBundler = class {
|
|
|
350
1042
|
hmr: false
|
|
351
1043
|
},
|
|
352
1044
|
logLevel: "error",
|
|
1045
|
+
resolve: aliasEntries.length > 0 ? {
|
|
1046
|
+
alias: aliasEntries,
|
|
1047
|
+
dedupe: ["react", "react-dom", "vue", "svelte"]
|
|
1048
|
+
} : void 0,
|
|
353
1049
|
plugins: [
|
|
354
1050
|
{
|
|
355
1051
|
name: "frameshot-virtual",
|
|
@@ -396,10 +1092,11 @@ var ViteBundler = class {
|
|
|
396
1092
|
}
|
|
397
1093
|
});
|
|
398
1094
|
}
|
|
399
|
-
}
|
|
1095
|
+
},
|
|
1096
|
+
...adapterPlugins
|
|
400
1097
|
],
|
|
401
1098
|
optimizeDeps: {
|
|
402
|
-
include:
|
|
1099
|
+
include: adapter.getOptimizeDeps(project.root)
|
|
403
1100
|
}
|
|
404
1101
|
});
|
|
405
1102
|
await server.listen();
|
|
@@ -408,15 +1105,16 @@ var ViteBundler = class {
|
|
|
408
1105
|
const cached = {
|
|
409
1106
|
server,
|
|
410
1107
|
port,
|
|
411
|
-
currentEntry: ""
|
|
1108
|
+
currentEntry: "",
|
|
1109
|
+
adapter
|
|
412
1110
|
};
|
|
413
1111
|
this.servers.set(project.root, cached);
|
|
414
1112
|
return cached;
|
|
415
1113
|
}
|
|
416
|
-
async
|
|
1114
|
+
async importFrameshotVite() {
|
|
417
1115
|
try {
|
|
418
|
-
const
|
|
419
|
-
const vitePath =
|
|
1116
|
+
const requireFromHere = createRequire(import.meta.url);
|
|
1117
|
+
const vitePath = requireFromHere.resolve("vite");
|
|
420
1118
|
const mod = await import(vitePath);
|
|
421
1119
|
if (mod && typeof mod === "object" && "createServer" in mod) {
|
|
422
1120
|
return mod;
|
|
@@ -432,54 +1130,27 @@ var ViteBundler = class {
|
|
|
432
1130
|
}
|
|
433
1131
|
return null;
|
|
434
1132
|
}
|
|
1133
|
+
async importVite(projectRoot) {
|
|
1134
|
+
try {
|
|
1135
|
+
const require2 = createRequire(join18(projectRoot, "package.json"));
|
|
1136
|
+
const vitePath = require2.resolve("vite");
|
|
1137
|
+
const mod = await import(vitePath);
|
|
1138
|
+
if (mod && typeof mod === "object" && "createServer" in mod) {
|
|
1139
|
+
return mod;
|
|
1140
|
+
}
|
|
1141
|
+
} catch {
|
|
1142
|
+
}
|
|
1143
|
+
return this.importFrameshotVite();
|
|
1144
|
+
}
|
|
435
1145
|
hasPackage(projectRoot, name) {
|
|
436
1146
|
try {
|
|
437
|
-
const require2 = createRequire(
|
|
1147
|
+
const require2 = createRequire(join18(projectRoot, "package.json"));
|
|
438
1148
|
require2.resolve(name);
|
|
439
1149
|
return true;
|
|
440
1150
|
} catch {
|
|
441
1151
|
return false;
|
|
442
1152
|
}
|
|
443
1153
|
}
|
|
444
|
-
generateEntry(componentPath, framework, props, projectRoot) {
|
|
445
|
-
const propsJson = props ? JSON.stringify(props) : "{}";
|
|
446
|
-
const cssImport = projectRoot ? this.findGlobalCss(projectRoot) : "";
|
|
447
|
-
switch (framework) {
|
|
448
|
-
case "react": {
|
|
449
|
-
const hasRouter = projectRoot && this.hasPackage(projectRoot, "react-router-dom");
|
|
450
|
-
const routerImport = hasRouter ? `import { MemoryRouter } from "react-router-dom";` : "";
|
|
451
|
-
const wrap = hasRouter ? (inner) => `createElement(MemoryRouter, null, ${inner})` : (inner) => inner;
|
|
452
|
-
return `${cssImport}
|
|
453
|
-
import { createElement, Suspense } from "react";
|
|
454
|
-
import { createRoot } from "react-dom/client";
|
|
455
|
-
${routerImport}
|
|
456
|
-
import Component from "${componentPath}";
|
|
457
|
-
const props = ${propsJson};
|
|
458
|
-
const root = createRoot(document.getElementById("app"));
|
|
459
|
-
root.render(${wrap(`createElement(Suspense, { fallback: createElement("div", null, "Loading...") }, createElement(Component, props))`)});
|
|
460
|
-
`;
|
|
461
|
-
}
|
|
462
|
-
case "vue":
|
|
463
|
-
return `${cssImport}
|
|
464
|
-
import { createApp } from "vue";
|
|
465
|
-
import Component from "${componentPath}";
|
|
466
|
-
const props = ${propsJson};
|
|
467
|
-
createApp(Component, props).mount("#app");
|
|
468
|
-
`;
|
|
469
|
-
case "svelte":
|
|
470
|
-
return `${cssImport}
|
|
471
|
-
import Component from "${componentPath}";
|
|
472
|
-
const props = ${propsJson};
|
|
473
|
-
new Component({ target: document.getElementById("app"), props });
|
|
474
|
-
`;
|
|
475
|
-
default:
|
|
476
|
-
return `${cssImport}
|
|
477
|
-
const res = await fetch("${componentPath}");
|
|
478
|
-
const html = await res.text();
|
|
479
|
-
document.getElementById("app").innerHTML = html;
|
|
480
|
-
`;
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
1154
|
findGlobalCss(projectRoot) {
|
|
484
1155
|
const candidates = [
|
|
485
1156
|
"src/index.css",
|
|
@@ -491,25 +1162,13 @@ document.getElementById("app").innerHTML = html;
|
|
|
491
1162
|
"app/globals.css"
|
|
492
1163
|
];
|
|
493
1164
|
for (const candidate of candidates) {
|
|
494
|
-
const fullPath =
|
|
495
|
-
if (
|
|
1165
|
+
const fullPath = join18(projectRoot, candidate);
|
|
1166
|
+
if (existsSync4(fullPath)) {
|
|
496
1167
|
return `import "${fullPath}";`;
|
|
497
1168
|
}
|
|
498
1169
|
}
|
|
499
1170
|
return "";
|
|
500
1171
|
}
|
|
501
|
-
getOptimizeDepsInclude(framework) {
|
|
502
|
-
switch (framework) {
|
|
503
|
-
case "react":
|
|
504
|
-
return ["react", "react-dom/client"];
|
|
505
|
-
case "vue":
|
|
506
|
-
return ["vue"];
|
|
507
|
-
case "svelte":
|
|
508
|
-
return [];
|
|
509
|
-
default:
|
|
510
|
-
return [];
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
1172
|
};
|
|
514
1173
|
|
|
515
1174
|
// src/use-cases/audit.ts
|
|
@@ -591,34 +1250,9 @@ var AuditUseCase = class {
|
|
|
591
1250
|
}
|
|
592
1251
|
};
|
|
593
1252
|
|
|
594
|
-
// src/domain/types.ts
|
|
595
|
-
var DEFAULT_RENDER_OPTIONS = {
|
|
596
|
-
viewport: { width: 1280, height: 800 },
|
|
597
|
-
engines: ["chromium"],
|
|
598
|
-
fullPage: true,
|
|
599
|
-
darkMode: false,
|
|
600
|
-
css: "",
|
|
601
|
-
tailwindVersion: "3",
|
|
602
|
-
waitFor: 0,
|
|
603
|
-
autoFit: false
|
|
604
|
-
};
|
|
605
|
-
var DEVICE_PRESETS = {
|
|
606
|
-
mobile: { width: 375, height: 667 },
|
|
607
|
-
tablet: { width: 768, height: 1024 },
|
|
608
|
-
desktop: { width: 1280, height: 800 }
|
|
609
|
-
};
|
|
610
|
-
var EXT_TO_FRAMEWORK = {
|
|
611
|
-
".jsx": "react",
|
|
612
|
-
".tsx": "react",
|
|
613
|
-
".vue": "vue",
|
|
614
|
-
".svelte": "svelte",
|
|
615
|
-
".html": "html",
|
|
616
|
-
".htm": "html"
|
|
617
|
-
};
|
|
618
|
-
|
|
619
1253
|
// src/use-cases/catalog.ts
|
|
620
1254
|
import { readdirSync, statSync } from "fs";
|
|
621
|
-
import { extname, join as
|
|
1255
|
+
import { extname, join as join19, relative } from "path";
|
|
622
1256
|
var CatalogUseCase = class {
|
|
623
1257
|
constructor(renderUseCase) {
|
|
624
1258
|
this.renderUseCase = renderUseCase;
|
|
@@ -676,7 +1310,7 @@ var CatalogUseCase = class {
|
|
|
676
1310
|
const entries = readdirSync(dir);
|
|
677
1311
|
for (const entry of entries) {
|
|
678
1312
|
if (entry.startsWith(".") || entry === "node_modules") continue;
|
|
679
|
-
const fullPath =
|
|
1313
|
+
const fullPath = join19(dir, entry);
|
|
680
1314
|
const stat = statSync(fullPath);
|
|
681
1315
|
if (stat.isDirectory() && recursive) {
|
|
682
1316
|
files.push(...this.scanDirectory(fullPath, recursive));
|
|
@@ -740,7 +1374,7 @@ var DiffUseCase = class {
|
|
|
740
1374
|
};
|
|
741
1375
|
|
|
742
1376
|
// src/use-cases/render.ts
|
|
743
|
-
import { readFileSync as
|
|
1377
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
744
1378
|
import { extname as extname2 } from "path";
|
|
745
1379
|
|
|
746
1380
|
// src/infrastructure/page-utils.ts
|
|
@@ -825,7 +1459,7 @@ var RenderUseCase = class {
|
|
|
825
1459
|
);
|
|
826
1460
|
}
|
|
827
1461
|
}
|
|
828
|
-
const code =
|
|
1462
|
+
const code = readFileSync3(filePath, "utf-8");
|
|
829
1463
|
const results = await this.render(code, framework, options);
|
|
830
1464
|
return { results, mode: "cdn" };
|
|
831
1465
|
}
|
|
@@ -962,6 +1596,23 @@ var RenderUseCase = class {
|
|
|
962
1596
|
const viewport = options.autoFit ? await this.resolveAutoFitViewport(page, url, options) : options.viewport;
|
|
963
1597
|
await page.setViewportSize(viewport);
|
|
964
1598
|
await this.navigateAndWait(page, url, options.waitFor);
|
|
1599
|
+
const overlayState = await page.evaluate(() => {
|
|
1600
|
+
const overlay = document.querySelector("vite-error-overlay");
|
|
1601
|
+
const app = document.getElementById("app");
|
|
1602
|
+
const hasContent = !!app?.firstElementChild;
|
|
1603
|
+
if (!overlay) return { overlay: null, hasContent };
|
|
1604
|
+
const shadow = overlay.shadowRoot;
|
|
1605
|
+
const messageEl = shadow?.querySelector(".message-body");
|
|
1606
|
+
return {
|
|
1607
|
+
overlay: messageEl?.textContent?.trim() ?? "Vite error overlay",
|
|
1608
|
+
hasContent
|
|
1609
|
+
};
|
|
1610
|
+
}).catch(() => ({ overlay: null, hasContent: false }));
|
|
1611
|
+
if (overlayState.overlay && !overlayState.hasContent) {
|
|
1612
|
+
cleanup();
|
|
1613
|
+
this.pool.releasePage(engine, page);
|
|
1614
|
+
throw new Error(`Vite compilation error: ${overlayState.overlay}`);
|
|
1615
|
+
}
|
|
965
1616
|
const result = await takeScreenshot(
|
|
966
1617
|
page,
|
|
967
1618
|
engine,
|
|
@@ -1106,6 +1757,8 @@ var SnapshotUseCase = class {
|
|
|
1106
1757
|
|
|
1107
1758
|
export {
|
|
1108
1759
|
__export,
|
|
1760
|
+
DEVICE_PRESETS,
|
|
1761
|
+
EXT_TO_FRAMEWORK,
|
|
1109
1762
|
BrowserPool,
|
|
1110
1763
|
HtmlBuilder,
|
|
1111
1764
|
ImageComparator,
|
|
@@ -1113,8 +1766,6 @@ export {
|
|
|
1113
1766
|
ProjectDetector,
|
|
1114
1767
|
ViteBundler,
|
|
1115
1768
|
AuditUseCase,
|
|
1116
|
-
DEVICE_PRESETS,
|
|
1117
|
-
EXT_TO_FRAMEWORK,
|
|
1118
1769
|
CatalogUseCase,
|
|
1119
1770
|
DiffUseCase,
|
|
1120
1771
|
RenderUseCase,
|