@agent-scope/cli 1.0.1 → 1.1.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/dist/index.cjs CHANGED
@@ -8,6 +8,7 @@ var playwright = require('@agent-scope/playwright');
8
8
  var playwright$1 = require('playwright');
9
9
  var render = require('@agent-scope/render');
10
10
  var esbuild = require('esbuild');
11
+ var module$1 = require('module');
11
12
 
12
13
  function _interopNamespace(e) {
13
14
  if (e && e.__esModule) return e;
@@ -337,7 +338,7 @@ function writeReportToFile(report, outputPath, pretty) {
337
338
  const json = pretty ? JSON.stringify(report, null, 2) : JSON.stringify(report);
338
339
  fs.writeFileSync(outputPath, json, "utf-8");
339
340
  }
340
- async function buildComponentHarness(filePath, componentName, props, viewportWidth) {
341
+ async function buildComponentHarness(filePath, componentName, props, viewportWidth, projectCss) {
341
342
  const bundledScript = await bundleComponentToIIFE(filePath, componentName, props);
342
343
  return wrapInHtml(bundledScript, viewportWidth);
343
344
  }
@@ -425,7 +426,8 @@ ${msg}`);
425
426
  }
426
427
  return outputFile.text;
427
428
  }
428
- function wrapInHtml(bundledScript, viewportWidth) {
429
+ function wrapInHtml(bundledScript, viewportWidth, projectCss) {
430
+ const projectStyleBlock = "";
429
431
  return `<!DOCTYPE html>
430
432
  <html lang="en">
431
433
  <head>
@@ -436,6 +438,7 @@ function wrapInHtml(bundledScript, viewportWidth) {
436
438
  html, body { margin: 0; padding: 0; background: #fff; font-family: system-ui, sans-serif; }
437
439
  #scope-root { display: inline-block; min-width: 1px; min-height: 1px; }
438
440
  </style>
441
+ ${projectStyleBlock}
439
442
  </head>
440
443
  <body>
441
444
  <div id="scope-root" data-reactscope-root></div>
@@ -603,6 +606,121 @@ function csvEscape(value) {
603
606
  }
604
607
  return value;
605
608
  }
609
+ var CONFIG_FILENAMES = [
610
+ ".reactscope/config.json",
611
+ ".reactscope/config.js",
612
+ ".reactscope/config.mjs"
613
+ ];
614
+ var STYLE_ENTRY_CANDIDATES = [
615
+ "src/index.css",
616
+ "src/globals.css",
617
+ "app/globals.css",
618
+ "app/index.css",
619
+ "styles/index.css",
620
+ "index.css"
621
+ ];
622
+ var TAILWIND_IMPORT = /@import\s+["']tailwindcss["']\s*;?/;
623
+ var compilerCache = null;
624
+ function getCachedBuild(cwd) {
625
+ if (compilerCache !== null && path.resolve(compilerCache.cwd) === path.resolve(cwd)) {
626
+ return compilerCache.build;
627
+ }
628
+ return null;
629
+ }
630
+ function findStylesEntry(cwd) {
631
+ for (const name of CONFIG_FILENAMES) {
632
+ const p = path.resolve(cwd, name);
633
+ if (!fs.existsSync(p)) continue;
634
+ try {
635
+ if (name.endsWith(".json")) {
636
+ const raw = fs.readFileSync(p, "utf-8");
637
+ const data = JSON.parse(raw);
638
+ const scope = data.scope;
639
+ const entry = scope?.stylesEntry ?? data.stylesEntry;
640
+ if (typeof entry === "string") {
641
+ const full = path.resolve(cwd, entry);
642
+ if (fs.existsSync(full)) return full;
643
+ }
644
+ }
645
+ } catch {
646
+ }
647
+ }
648
+ const pkgPath = path.resolve(cwd, "package.json");
649
+ if (fs.existsSync(pkgPath)) {
650
+ try {
651
+ const raw = fs.readFileSync(pkgPath, "utf-8");
652
+ const pkg = JSON.parse(raw);
653
+ const entry = pkg.scope?.stylesEntry;
654
+ if (typeof entry === "string") {
655
+ const full = path.resolve(cwd, entry);
656
+ if (fs.existsSync(full)) return full;
657
+ }
658
+ } catch {
659
+ }
660
+ }
661
+ for (const candidate of STYLE_ENTRY_CANDIDATES) {
662
+ const full = path.resolve(cwd, candidate);
663
+ if (fs.existsSync(full)) {
664
+ try {
665
+ const content = fs.readFileSync(full, "utf-8");
666
+ if (TAILWIND_IMPORT.test(content)) return full;
667
+ } catch {
668
+ }
669
+ }
670
+ }
671
+ return null;
672
+ }
673
+ async function getTailwindCompiler(cwd) {
674
+ const cached = getCachedBuild(cwd);
675
+ if (cached !== null) return cached;
676
+ const entryPath = findStylesEntry(cwd);
677
+ if (entryPath === null) return null;
678
+ let compile;
679
+ try {
680
+ const require2 = module$1.createRequire(path.resolve(cwd, "package.json"));
681
+ const tailwind = require2("tailwindcss");
682
+ const fn = tailwind.compile;
683
+ if (typeof fn !== "function") return null;
684
+ compile = fn;
685
+ } catch {
686
+ return null;
687
+ }
688
+ const entryContent = fs.readFileSync(entryPath, "utf-8");
689
+ const loadStylesheet = async (id, base) => {
690
+ if (id === "tailwindcss") {
691
+ const nodeModules = path.resolve(cwd, "node_modules");
692
+ const tailwindCssPath = path.resolve(nodeModules, "tailwindcss", "index.css");
693
+ if (!fs.existsSync(tailwindCssPath)) {
694
+ throw new Error(
695
+ `Tailwind v4: tailwindcss package not found at ${tailwindCssPath}. Install with: npm install tailwindcss`
696
+ );
697
+ }
698
+ const content = fs.readFileSync(tailwindCssPath, "utf-8");
699
+ return { path: "virtual:tailwindcss/index.css", base, content };
700
+ }
701
+ const full = path.resolve(base, id);
702
+ if (fs.existsSync(full)) {
703
+ const content = fs.readFileSync(full, "utf-8");
704
+ return { path: full, base: path.resolve(full, ".."), content };
705
+ }
706
+ throw new Error(`Tailwind v4: could not load stylesheet: ${id} (base: ${base})`);
707
+ };
708
+ const result = await compile(entryContent, {
709
+ base: cwd,
710
+ from: entryPath,
711
+ loadStylesheet
712
+ });
713
+ const build2 = result.build.bind(result);
714
+ compilerCache = { cwd, build: build2 };
715
+ return build2;
716
+ }
717
+ async function getCompiledCssForClasses(cwd, classes) {
718
+ const build2 = await getTailwindCompiler(cwd);
719
+ if (build2 === null) return null;
720
+ const deduped = [...new Set(classes)].filter(Boolean);
721
+ if (deduped.length === 0) return null;
722
+ return build2(deduped);
723
+ }
606
724
 
607
725
  // src/render-commands.ts
608
726
  var MANIFEST_PATH2 = ".reactscope/manifest.json";
@@ -657,6 +775,20 @@ function buildRenderer(filePath, componentName, viewportWidth, viewportHeight) {
657
775
  if (renderError !== null) {
658
776
  throw new Error(`Component render error: ${renderError}`);
659
777
  }
778
+ const rootDir = process.cwd();
779
+ const classes = await page.evaluate(() => {
780
+ const set = /* @__PURE__ */ new Set();
781
+ document.querySelectorAll("[class]").forEach((el) => {
782
+ for (const c of el.className.split(/\s+/)) {
783
+ if (c) set.add(c);
784
+ }
785
+ });
786
+ return [...set];
787
+ });
788
+ const projectCss = await getCompiledCssForClasses(rootDir, classes);
789
+ if (projectCss != null && projectCss.length > 0) {
790
+ await page.addStyleTag({ content: projectCss });
791
+ }
660
792
  const renderTimeMs = performance.now() - startMs;
661
793
  const rootLocator = page.locator("[data-reactscope-root]");
662
794
  const boundingBox = await rootLocator.boundingBox();
@@ -665,13 +797,19 @@ function buildRenderer(filePath, componentName, viewportWidth, viewportHeight) {
665
797
  `Component "${componentName}" rendered with zero bounding box \u2014 it may be invisible or not mounted`
666
798
  );
667
799
  }
800
+ const PAD = 24;
801
+ const MIN_W = 320;
802
+ const MIN_H = 200;
803
+ const clipX = Math.max(0, boundingBox.x - PAD);
804
+ const clipY = Math.max(0, boundingBox.y - PAD);
805
+ const rawW = boundingBox.width + PAD * 2;
806
+ const rawH = boundingBox.height + PAD * 2;
807
+ const clipW = Math.max(rawW, MIN_W);
808
+ const clipH = Math.max(rawH, MIN_H);
809
+ const safeW = Math.min(clipW, viewportWidth - clipX);
810
+ const safeH = Math.min(clipH, viewportHeight - clipY);
668
811
  const screenshot = await page.screenshot({
669
- clip: {
670
- x: boundingBox.x,
671
- y: boundingBox.y,
672
- width: boundingBox.width,
673
- height: boundingBox.height
674
- },
812
+ clip: { x: clipX, y: clipY, width: safeW, height: safeH },
675
813
  type: "png"
676
814
  });
677
815
  const computedStyles = {};
@@ -698,8 +836,8 @@ function buildRenderer(filePath, componentName, viewportWidth, viewportHeight) {
698
836
  computedStyles["[data-reactscope-root] > *"] = styles;
699
837
  return {
700
838
  screenshot,
701
- width: Math.round(boundingBox.width),
702
- height: Math.round(boundingBox.height),
839
+ width: Math.round(safeW),
840
+ height: Math.round(safeH),
703
841
  renderTimeMs,
704
842
  computedStyles
705
843
  };