Ctrl K
ComunidadGuías…

scripts/build-chickpea-product-mark.mjs

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import { readFile, writeFile } from 'node:fs/promises';
import { createHash } from 'node:crypto';
import { join } from 'node:path';
import { fileURLToPath } from 'node:url';
import sharp from 'sharp';

const ROOT = fileURLToPath(new URL('..', import.meta.url));
const OUTPUT = join(ROOT, 'src', 'brand', 'chickpea-mark.generated.ts');
const MARK_MASTER = join(ROOT, 'assets', 'chickpea-mark.png');
const WORDMARK_MASTER = join(ROOT, 'assets', 'chickpea-wordmark-mask.png');
const PNG_OPTIONS = { compressionLevel: 9, adaptiveFiltering: true, palette: false };
const DERIVATIVES = [
  { source: MARK_MASTER, file: 'chickpea-mark-128.png', width: 128, height: 128 },
  { source: MARK_MASTER, file: 'chickpea-favicon-32.png', width: 32, height: 32 },
  { source: WORDMARK_MASTER, file: 'chickpea-wordmark-512.png', width: 512, height: 126 },
  // White-on-dark counterpart per BRAND.md (cocoa on light surfaces, white on
  // dark); the README's <picture> lockup swaps to it under dark color schemes.
  { source: WORDMARK_MASTER, file: 'chickpea-wordmark-512-dark.png', width: 512, height: 126, tint: '#ffffff' },
];
const ASSETS = [
  { exportName: 'CHICKPEA_MARK_URL', file: 'chickpea-mark-128.png', width: 128, height: 128 },
  { exportName: 'CHICKPEA_FAVICON_URL', file: 'chickpea-favicon-32.png', width: 32, height: 32 },
  { exportName: 'CHICKPEA_WORDMARK_URL', file: 'chickpea-wordmark-512.png', width: 512, height: 126 },
];

async function validateMaster(file, width, height, label) {
  const image = sharp(file);
  const metadata = await image.metadata();
  if (metadata.format !== 'png' || metadata.width !== width || metadata.height !== height) {
    throw new Error(
      `${label} must be a ${width}x${height} PNG, got ${metadata.width}x${metadata.height} ${metadata.format}`,
    );
  }
  return image;
}

await validateMaster(MARK_MASTER, 512, 512, 'chickpea-mark.png');
const wordmark = await validateMaster(
  WORDMARK_MASTER,
  2106,
  518,
  'chickpea-wordmark-mask.png',
);
const wordmarkStats = await wordmark.ensureAlpha().stats();
const wordmarkAlpha = wordmarkStats.channels[3];
if (wordmarkAlpha.min !== 0 || wordmarkAlpha.max !== 255) {
  throw new Error('chickpea-wordmark-mask.png must contain transparent and opaque pixels');
}

for (const derivative of DERIVATIVES) {
  const output = join(ROOT, 'assets', derivative.file);
  if (derivative.tint) {
    const alpha = await sharp(derivative.source)
      .resize(derivative.width, derivative.height, { fit: 'fill' })
      .ensureAlpha()
      .extractChannel(3)
      .toBuffer();
    await sharp({
      create: {
        width: derivative.width,
        height: derivative.height,
        channels: 3,
        background: derivative.tint,
      },
    })
      .joinChannel(alpha)
      .png(PNG_OPTIONS)
      .toFile(output);
    continue;
  }
  await sharp(derivative.source)
    .resize(derivative.width, derivative.height, { fit: 'fill' })
    .png(PNG_OPTIONS)
    .toFile(output);
}

const exports = [];
for (const asset of ASSETS) {
  const bytes = await readFile(join(ROOT, 'assets', asset.file));
  await validateMaster(join(ROOT, 'assets', asset.file), asset.width, asset.height, asset.file);
  const version = createHash('sha256').update(bytes).digest('hex').slice(0, 12);
  exports.push(
    `export const ${asset.exportName} = ${JSON.stringify(`/${asset.file}?v=${version}`)};`,
  );
}

const source =
  '// Generated by scripts/build-chickpea-product-mark.mjs. Do not edit by hand.\n' +
  `${exports.join('\n\n')}\n`;

await writeFile(OUTPUT, source);
console.log(`Wrote ${ASSETS.length} Chickpea product-mark assets to ${OUTPUT}`);