@saeroon/cli 0.2.7 → 0.2.8

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 (2) hide show
  1. package/dist/index.js +181 -37
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2514,19 +2514,27 @@ var ELEMENT_TYPE_PROPS = {
2514
2514
  "pre",
2515
2515
  "code",
2516
2516
  "progress",
2517
- "meter"
2517
+ "meter",
2518
+ "output",
2519
+ "label",
2520
+ "search",
2521
+ "time",
2522
+ "mark",
2523
+ "abbr"
2518
2524
  ]),
2519
2525
  coerce: () => null
2520
2526
  }
2521
2527
  };
2522
2528
  var VALID_BLOCK_TYPES = /* @__PURE__ */ new Set([
2523
- // Primitives (11)
2529
+ // Primitives (13)
2524
2530
  "container",
2525
2531
  "text-block",
2532
+ "rich-text-block",
2526
2533
  "heading-block",
2527
2534
  "button-block",
2528
2535
  "image-block",
2529
2536
  "video-block",
2537
+ "audio-block",
2530
2538
  "embed-block",
2531
2539
  "icon-block",
2532
2540
  "input-block",
@@ -4032,32 +4040,43 @@ var EXTRACTION_SCRIPT = `
4032
4040
  let expanded = false;
4033
4041
  els.forEach(el => {
4034
4042
  const tag = el.tagName.toLowerCase();
4035
- // header, footer, nav, section, article \uB4F1 \uC2DC\uB9E8\uD2F1 \uD0DC\uADF8\uB294 \uC720\uC9C0
4036
- if (['header', 'footer', 'nav', 'section', 'article', 'aside', 'main'].includes(tag)) {
4037
- // main\uC740 \uADF8 \uC548\uC758 \uC790\uC2DD\uC744 \uD3BC\uCE68
4038
- if (tag === 'main') {
4039
- const children = Array.from(el.children).filter(c => c.getBoundingClientRect().height >= 10);
4040
- if (children.length > 1) {
4041
- result.push(...children);
4042
- expanded = true;
4043
- } else {
4044
- result.push(el);
4045
- }
4043
+ // \uC2DC\uB9E8\uD2F1 \uD0DC\uADF8: header/footer/nav/section/article/aside\uB294 \uC139\uC158\uC73C\uB85C \uC720\uC9C0
4044
+ const keepTags = new Set(['header', 'footer', 'nav', 'section', 'article', 'aside']);
4045
+ // main/form\uC740 \uB798\uD37C \uC5ED\uD560\uC774 \uB9CE\uC74C \u2014 \uC790\uC2DD\uC774 \uC5EC\uB7EC \uAC1C\uBA74 \uD3BC\uCE68
4046
+ const unwrapTags = new Set(['main', 'form']);
4047
+
4048
+ if (keepTags.has(tag)) {
4049
+ result.push(el);
4050
+ return;
4051
+ }
4052
+
4053
+ const children = Array.from(el.children).filter(c => c.getBoundingClientRect().height >= 10);
4054
+
4055
+ if (unwrapTags.has(tag)) {
4056
+ // main/form: \uC790\uC2DD\uC774 \uC5EC\uB7EC \uAC1C\uBA74 \uD3BC\uCE68
4057
+ if (children.length > 1) {
4058
+ result.push(...children);
4059
+ expanded = true;
4046
4060
  } else {
4047
4061
  result.push(el);
4048
4062
  }
4049
4063
  return;
4050
4064
  }
4051
- // div \uB798\uD37C \uBC97\uAE30\uAE30
4052
- const children = Array.from(el.children).filter(c => c.getBoundingClientRect().height >= 10);
4053
- if (children.length === 1) {
4054
- // \uB2E8\uC77C \uC790\uC2DD wrapper \u2192 \uBC97\uAE40
4055
- result.push(...children);
4056
- expanded = true;
4057
- } else if (children.length > 1 && depth >= 4) {
4058
- // \uAE4A\uC774 \uC0C1\uC704 2\uB2E8\uACC4(depth 5\u21924)\uC5D0\uC11C\uB9CC multi-child \uD655\uC7A5
4065
+
4066
+ // div \uB4F1 \uBE44\uC2DC\uB9E8\uD2F1 \uB798\uD37C \uBC97\uAE30\uAE30
4067
+ if (children.length <= 1 && children.length > 0) {
4059
4068
  result.push(...children);
4060
4069
  expanded = true;
4070
+ } else if (children.length > 1) {
4071
+ // \uD398\uC774\uC9C0 \uB192\uC774 \uB300\uBD80\uBD84\uC744 \uCC28\uC9C0\uD558\uB294 \uB798\uD37C\uB294 \uD3BC\uCE68
4072
+ const elHeight = el.getBoundingClientRect().height;
4073
+ const pageHeight = document.body.scrollHeight || 1;
4074
+ if (elHeight > pageHeight * 0.5) {
4075
+ result.push(...children);
4076
+ expanded = true;
4077
+ } else {
4078
+ result.push(el);
4079
+ }
4061
4080
  } else {
4062
4081
  result.push(el);
4063
4082
  }
@@ -4110,7 +4129,18 @@ var EXTRACTION_SCRIPT = `
4110
4129
  });
4111
4130
 
4112
4131
  // heading hierarchy (\uC2DC\uB9E8\uD2F1 + \uB300\uD615 \uD3F0\uD2B8 pseudo-heading)
4113
- const headings = Array.from(document.querySelectorAll('h1, h2, h3, h4, h5, h6'));
4132
+ // \uD31D\uC5C5/\uBAA8\uB2EC/dialog \uB0B4\uBD80 heading \uC81C\uC678
4133
+ const headings = Array.from(document.querySelectorAll('h1, h2, h3, h4, h5, h6')).filter(h => {
4134
+ const popup = h.closest('dialog, [class*=popup], [class*=modal], [class*=layer], [role=dialog], [style*="display: none"], [style*="display:none"]');
4135
+ if (popup) {
4136
+ const rect = popup.getBoundingClientRect();
4137
+ // \uD654\uBA74 \uBC16\uC774\uAC70\uB098 display:none\uC774\uBA74 \uD31D\uC5C5 heading
4138
+ if (rect.width === 0 || rect.height === 0) return false;
4139
+ // \uD31D\uC5C5\uC774 \uD654\uBA74 \uAC00\uC6B4\uB370 \uB5A0 \uC788\uC73C\uBA74 (\uC804\uCCB4 \uB108\uBE44\uC758 90% \uBBF8\uB9CC) \uD31D\uC5C5 heading
4140
+ if (rect.width < window.innerWidth * 0.9) return false;
4141
+ }
4142
+ return true;
4143
+ });
4114
4144
  const headingHierarchy = headings.map(h => h.tagName.toLowerCase() + ': ' + (h.textContent || '').trim().slice(0, 80));
4115
4145
 
4116
4146
  // \uC2DC\uB9E8\uD2F1 heading\uC774 \uBD80\uC871\uD558\uBA74 \uD070 \uD3F0\uD2B8 \uC694\uC18C\uB97C pseudo-heading\uC73C\uB85C \uBCF4\uC644
@@ -4341,10 +4371,40 @@ var EXTRACTION_SCRIPT = `
4341
4371
  const hasHoverEffects = transitionProps.size > 0;
4342
4372
 
4343
4373
  // \u2500\u2500 4. Images \u2500\u2500
4374
+ // inline style + \uC8FC\uC694 \uCEE8\uD14C\uC774\uB108\uC758 computed background-image \uD0D0\uC0C9
4344
4375
  const imgEls = document.querySelectorAll('img, picture source, [style*="background-image"]');
4345
4376
  const images = [];
4346
4377
  const seenImageSrcs = new Set();
4347
4378
 
4379
+ // computed background-image\uAC00 \uC788\uB294 \uCEE8\uD14C\uC774\uB108 \uC694\uC18C \uCD94\uAC00 \uD0D0\uC0C9
4380
+ const bgCandidates = document.querySelectorAll('div, section, header, footer, main, article, aside, figure, span, a');
4381
+ const bgCandidateArr = Array.from(bgCandidates).filter((_, i) => i % Math.max(1, Math.ceil(bgCandidates.length / 300)) === 0);
4382
+ bgCandidateArr.forEach(el => {
4383
+ const bg = getComputedProp(el, 'background-image');
4384
+ if (bg && bg !== 'none') {
4385
+ const match = bg.match(/url\\(["']?(.+?)["']?\\)/);
4386
+ if (match && match[1] && !match[1].startsWith('data:') && !match[1].includes('.svg')) {
4387
+ const src = match[1];
4388
+ if (seenImageSrcs.has(src)) return;
4389
+ seenImageSrcs.add(src);
4390
+ const rect = el.getBoundingClientRect();
4391
+ if (rect.width < 20 || rect.height < 20) return;
4392
+ const parentSection = el.closest('section, header, footer, main, [class*=hero], [class*=banner]');
4393
+ const parentRole = parentSection ? inferSectionRole(parentSection) : 'unknown';
4394
+ images.push({
4395
+ src: src.slice(0, 500),
4396
+ alt: '',
4397
+ width: Math.round(rect.width),
4398
+ height: Math.round(rect.height),
4399
+ aspectRatio: guessAspectRatio(Math.round(rect.width), Math.round(rect.height)),
4400
+ role: rect.width > window.innerWidth * 0.8 && rect.height > 300 ? 'hero-bg' : 'background',
4401
+ dominantColor: '',
4402
+ position: parentRole,
4403
+ });
4404
+ }
4405
+ }
4406
+ });
4407
+
4348
4408
  imgEls.forEach(el => {
4349
4409
  let src = '';
4350
4410
  let alt = '';
@@ -4634,6 +4694,22 @@ function captureScreenshot(url, outputPath, width, height, timeout) {
4634
4694
  // \uD31D\uC5C5 \uC790\uB3D9 \uB2EB\uAE30
4635
4695
  await page.evaluate(() => { ${POPUP_DISMISS_SCRIPT} });
4636
4696
  await page.waitForTimeout(500);
4697
+ // scroll simulation \u2014 lazy-load/IntersectionObserver \uCF58\uD150\uCE20 \uD2B8\uB9AC\uAC70
4698
+ await page.evaluate(async () => {
4699
+ const delay = (ms) => new Promise(r => setTimeout(r, ms));
4700
+ const scrollHeight = document.body.scrollHeight;
4701
+ const viewportHeight = window.innerHeight;
4702
+ const step = Math.floor(viewportHeight * 0.7);
4703
+ for (let y = 0; y < scrollHeight; y += step) {
4704
+ window.scrollTo(0, y);
4705
+ await delay(300);
4706
+ }
4707
+ window.scrollTo(0, scrollHeight);
4708
+ await delay(500);
4709
+ window.scrollTo(0, 0);
4710
+ await delay(500);
4711
+ });
4712
+ await page.waitForTimeout(1000);
4637
4713
  await page.screenshot({ path: ${JSON.stringify(outputPath)}, fullPage: true });
4638
4714
  await browser.close();
4639
4715
  })().catch(e => {
@@ -4686,6 +4762,22 @@ async function extractPageData(url, timeout) {
4686
4762
  await page.evaluate(() => { ${POPUP_DISMISS_SCRIPT} });
4687
4763
  await page.waitForTimeout(500);
4688
4764
 
4765
+ // scroll simulation \u2014 lazy-load \uCF58\uD150\uCE20 \uD2B8\uB9AC\uAC70 (DOM \uCD94\uCD9C \uC804)
4766
+ await page.evaluate(async () => {
4767
+ const delay = (ms) => new Promise(r => setTimeout(r, ms));
4768
+ const scrollHeight = document.body.scrollHeight;
4769
+ const step = Math.floor(window.innerHeight * 0.7);
4770
+ for (let y = 0; y < scrollHeight; y += step) {
4771
+ window.scrollTo(0, y);
4772
+ await delay(300);
4773
+ }
4774
+ window.scrollTo(0, scrollHeight);
4775
+ await delay(500);
4776
+ window.scrollTo(0, 0);
4777
+ await delay(500);
4778
+ });
4779
+ await page.waitForTimeout(1000);
4780
+
4689
4781
  const data = await page.evaluate(${JSON.stringify(EXTRACTION_SCRIPT)});
4690
4782
  await browser.close();
4691
4783
  process.stdout.write(JSON.stringify(data));
@@ -5318,25 +5410,77 @@ function checkCommand(cmd) {
5318
5410
  }
5319
5411
  }
5320
5412
  function captureScreenshot2(url, outputPath, width, height) {
5321
- const result = spawnSync2("npx", [
5322
- "playwright",
5323
- "screenshot",
5324
- "--browser",
5325
- "chromium",
5326
- "--viewport-size",
5327
- `${width},${height}`,
5328
- "--wait-for-timeout",
5329
- "3000",
5330
- "--full-page",
5331
- url,
5332
- outputPath
5333
- ], {
5413
+ const scriptContent = `
5414
+ const { chromium } = require('playwright');
5415
+ (async () => {
5416
+ const browser = await chromium.launch({ headless: true });
5417
+ const context = await browser.newContext({
5418
+ viewport: { width: ${width}, height: ${height} },
5419
+ userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
5420
+ });
5421
+ const page = await context.newPage();
5422
+ await page.goto(${JSON.stringify(url)}, { waitUntil: 'domcontentloaded', timeout: 30000 });
5423
+ await page.waitForTimeout(3000);
5424
+ // \uD31D\uC5C5/\uBAA8\uB2EC \uC790\uB3D9 \uB2EB\uAE30
5425
+ await page.evaluate(() => {
5426
+ document.querySelectorAll('dialog[open]').forEach(d => d.close());
5427
+ ['[class*=popup] [class*=close]','[class*=modal] [class*=close]',
5428
+ '[aria-label*=close]','[aria-label*="\uB2EB\uAE30"]','[aria-label*=Close]',
5429
+ '.popup-close','.modal-close','.btn-close'].forEach(sel => {
5430
+ document.querySelectorAll(sel).forEach(btn => { try { btn.click(); } catch {} });
5431
+ });
5432
+ document.querySelectorAll('[class*=overlay],[class*=dimmed],.modal-backdrop').forEach(el => {
5433
+ if (el.getBoundingClientRect().width >= window.innerWidth * 0.8) el.style.display = 'none';
5434
+ });
5435
+ document.body.style.overflow = '';
5436
+ document.body.style.position = '';
5437
+ document.documentElement.style.overflow = '';
5438
+ });
5439
+ await page.waitForTimeout(500);
5440
+ // scroll simulation \u2014 lazy-load \uCF58\uD150\uCE20 \uD2B8\uB9AC\uAC70
5441
+ await page.evaluate(async () => {
5442
+ const delay = (ms) => new Promise(r => setTimeout(r, ms));
5443
+ const step = Math.floor(window.innerHeight * 0.7);
5444
+ for (let y = 0; y < document.body.scrollHeight; y += step) {
5445
+ window.scrollTo(0, y);
5446
+ await delay(300);
5447
+ }
5448
+ window.scrollTo(0, document.body.scrollHeight);
5449
+ await delay(500);
5450
+ window.scrollTo(0, 0);
5451
+ await delay(500);
5452
+ });
5453
+ await page.waitForTimeout(1000);
5454
+ await page.screenshot({ path: ${JSON.stringify(outputPath)}, fullPage: true });
5455
+ await browser.close();
5456
+ })().catch(e => {
5457
+ process.stderr.write(e.message);
5458
+ process.exit(1);
5459
+ });
5460
+ `;
5461
+ const result = spawnSync2("node", ["-e", scriptContent], {
5334
5462
  stdio: "pipe",
5335
- timeout: 6e4
5463
+ timeout: 9e4,
5464
+ env: { ...process.env }
5336
5465
  });
5337
5466
  if (result.status !== 0) {
5338
5467
  const stderr = result.stderr?.toString() ?? "";
5339
- throw new Error(`\uC2A4\uD06C\uB9B0\uC0F7 \uCEA1\uCC98 \uC2E4\uD328: ${stderr || "unknown error"}`);
5468
+ const fallback = spawnSync2("npx", [
5469
+ "playwright",
5470
+ "screenshot",
5471
+ "--browser",
5472
+ "chromium",
5473
+ "--viewport-size",
5474
+ `${width},${height}`,
5475
+ "--wait-for-timeout",
5476
+ "3000",
5477
+ "--full-page",
5478
+ url,
5479
+ outputPath
5480
+ ], { stdio: "pipe", timeout: 6e4 });
5481
+ if (fallback.status !== 0) {
5482
+ throw new Error(`\uC2A4\uD06C\uB9B0\uC0F7 \uCEA1\uCC98 \uC2E4\uD328: ${stderr || "unknown error"}`);
5483
+ }
5340
5484
  }
5341
5485
  }
5342
5486
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saeroon/cli",
3
- "version": "0.2.7",
3
+ "version": "0.2.8",
4
4
  "description": "Saeroon Hosting developer CLI — preview, validate, and deploy sites & templates",
5
5
  "private": false,
6
6
  "type": "module",