Skip to content

docs: Set height on all images#39461

Open
cpAdm wants to merge 2 commits intomicrosoft:mainfrom
cpAdm:docs-set-dimension-images
Open

docs: Set height on all images#39461
cpAdm wants to merge 2 commits intomicrosoft:mainfrom
cpAdm:docs-set-dimension-images

Conversation

@cpAdm
Copy link
Contributor

@cpAdm cpAdm commented Feb 27, 2026

Script used (AI prompted)

import { promises as fs } from 'node:fs';
import path from 'node:path';
import process from 'node:process';
import { imageSize } from 'image-size';

const ROOT_DIR = path.resolve('./docs/src');
const IMG_REGEX = /<img\b[^>]*\bsrc="([^"]+)"[^>]*>/gi;

const MAX_CONCURRENT_FILES = 8;
let active = 0;
const queue: (() => void)[] = [];

async function limit<T>(fn: () => Promise<T>): Promise<T> {
  if (active >= MAX_CONCURRENT_FILES)
    await new Promise<void>(resolve => queue.push(resolve));

  active++;

  try {
    return await fn();
  } finally {
    active--;
    queue.shift()?.();
  }
}

async function walk(dir: string): Promise<void> {
  const entries = await fs.readdir(dir, { withFileTypes: true });

  await Promise.all(
      entries.map(async entry => {
        const fullPath = path.join(dir, entry.name);

        if (entry.isDirectory())
          return walk(fullPath);

        if (entry.isFile() && fullPath.endsWith('.md'))
          return limit(() => processMarkdown(fullPath));
      })
  );
}

async function processMarkdown(filePath: string) {
  let content = await fs.readFile(filePath, 'utf8');
  let modified = false;

  const matches = [...content.matchAll(IMG_REGEX)];

  for (const match of matches) {
    const [original, url] = match;

    const altMatch = original.match(/\balt="([^"]*)"/i);
    const currentAlt = altMatch ? altMatch[1] : undefined;
    if (!currentAlt)
      console.warn(`⚠ Does not have alt: ${url}`);

    // Checking local files is not needed; plugin-ideal-image will take care of these
    if (url.startsWith('/'))
      continue;

    try {
      const dimensions = await getImageDimensions(url);

      if (!dimensions?.width || !dimensions?.height) {
        console.warn(`⚠ Could not determine size: ${url}`);
        continue;
      }

      let updatedTag = original;
      updatedTag = upsertAttr(updatedTag, 'width', dimensions.width);
      updatedTag = upsertAttr(updatedTag, 'height', dimensions.height);

      if (updatedTag !== original) {
        content = content.replace(original, updatedTag);
        modified = true;
        console.log(`✔ Updated: ${path.relative(ROOT_DIR, filePath)}${url} (${dimensions.width}x${dimensions.height})`);
      } else {
        console.log(`• Unchanged: ${url}`);
      }

    } catch {
      console.warn(`✖ Failed: ${url}`);
    }
  }

  if (modified)
    await fs.writeFile(filePath, content, 'utf8');
}

function upsertAttr(tag: string, name: string, value: number) {
  const attrRegex = new RegExp(`\\b${name}="([^"]+)"`, 'i');
  const match = tag.match(attrRegex);

  if (match) {
    const existingValue = match[1].trim();

    // Leave percentages untouched
    if (existingValue.endsWith('%'))
      return tag;

    if (/^\d+$/.test(existingValue))
      return tag.replace(attrRegex, `${name}="${value}"`);

    return tag;
  }

  return tag.replace(/<img\b/i, `<img ${name}="${value}"`);
}

async function getImageDimensions(url: string) {
  const response = await fetch(url);

  if (!response.ok)
    throw new Error(`HTTP ${response.status}`);

  const buffer = Buffer.from(await response.arrayBuffer());
  return imageSize(buffer);
}

async function main() {
  console.log(`Scanning: ${ROOT_DIR}`);
  await walk(ROOT_DIR);
  console.log('Done.');
}

main().catch(err => {
  console.error(err);
  process.exit(1);
});

references: #39296 (review), microsoft/playwright.dev#1899

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants