Ctrl K
ComunidadGuías…

scripts/verify-tailwind4.mjs

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

Archivo original ↗Preguntar a KiBot
// ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. =========
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import { spawnSync } from 'node:child_process';
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { fileURLToPath } from 'node:url';

const scriptDirectory = path.dirname(fileURLToPath(import.meta.url));
const repositoryRoot = path.resolve(scriptDirectory, '..');
const fixturePath = path.join(
  repositoryRoot,
  'src',
  'style',
  `.tailwind4-verify-${process.pid}.css`
);
const outputPath = path.join(
  os.tmpdir(),
  `eigent-tailwind4-verify-${process.pid}.css`
);
const tailwindBinary = path.join(
  repositoryRoot,
  'node_modules',
  '.bin',
  'tailwindcss'
);

const fixture = `@import './index.css';
@source inline('short:block max-lg:block smooth-shadow-md smooth-shadow-ring-md bg-ds-accent-strong-default bg-ds-neutral-default-default bg-ds-bg-neutral-default-default text-ds-text-base text-body-sm text-ds-ink-default-default border-ds-hairline-default-default ring-ds-ring-focus shadow-ds-elevation-control h-ds-control-md');
`;

const requiredOutput = [
  ['public Neutral fill utility', '.bg-ds-neutral-default-default'],
  ['compatibility Neutral fill utility', '.bg-ds-bg-neutral-default-default'],
  ['compatibility typography alias', '.text-body-sm'],
  ['legacy custom shadow utility', '.shadow-button-shadow'],
  ['Tailwind 4 replacement utility', '.shrink-0'],
  ['shadow-plugin utility', '.smooth-shadow-md'],
  ['shadow-plugin ring utility', '.smooth-shadow-ring-md'],
  ['custom short-screen media query', '@media (max-height: 800px)'],
  ['custom max-lg responsive utility', '.max-lg\\:block'],
  ['public accent fill utility', '.bg-ds-accent-strong-default'],
  ['public Ink utility', '.text-ds-ink-default-default'],
  ['public Hairline utility', '.border-ds-hairline-default-default'],
  ['public focus ring utility', '.ring-ds-ring-focus'],
  ['generated type role utility', '.text-ds-text-base'],
  ['semantic elevation utility', '.shadow-ds-elevation-control'],
  ['control height utility', '.h-ds-control-md'],
];

const forbiddenOutput = [
  ['unprocessed Tailwind theme directive', '@theme'],
  ['unprocessed Tailwind utility directive', '@utility'],
  ['unprocessed config directive', '@config'],
  ['unprocessed source directive', '@source'],
  ['invalid empty selector', ':where()'],
  ['invalid layer media query', '@media layer(utilities)'],
];

function fail(message) {
  process.stderr.write(`FAIL  ${message}\n`);
  process.exitCode = 1;
}

try {
  const tailwindVersion = JSON.parse(
    fs.readFileSync(
      path.join(repositoryRoot, 'node_modules', 'tailwindcss', 'package.json'),
      'utf8'
    )
  ).version;
  const shadowPluginVersion = JSON.parse(
    fs.readFileSync(
      path.join(
        repositoryRoot,
        'node_modules',
        'shadow-plugin',
        'package.json'
      ),
      'utf8'
    )
  ).version;

  if (!tailwindVersion.startsWith('4.')) {
    fail(`Expected Tailwind 4, found ${tailwindVersion}`);
  }
  if (!shadowPluginVersion.startsWith('2.')) {
    fail(`Expected shadow-plugin 2, found ${shadowPluginVersion}`);
  }

  fs.writeFileSync(fixturePath, fixture, 'utf8');
  const result = spawnSync(
    tailwindBinary,
    ['-i', fixturePath, '-o', outputPath],
    {
      cwd: repositoryRoot,
      encoding: 'utf8',
    }
  );

  if (result.status !== 0) {
    process.stderr.write(result.stdout);
    process.stderr.write(result.stderr);
    fail(`Tailwind compilation exited with status ${result.status}`);
  } else {
    if (/\b(?:warning|unexpected)\b/i.test(result.stderr)) {
      process.stderr.write(result.stderr);
      fail('Tailwind compilation emitted a CSS warning');
    }
    const css = fs.readFileSync(outputPath, 'utf8');
    for (const [label, sentinel] of requiredOutput) {
      if (!css.includes(sentinel)) fail(`Missing ${label}: ${sentinel}`);
    }
    for (const [label, sentinel] of forbiddenOutput) {
      if (css.includes(sentinel)) fail(`Found ${label}: ${sentinel}`);
    }
  }

  if (!process.exitCode) {
    process.stdout.write(
      `PASS  Tailwind ${tailwindVersion}, shadow-plugin ${shadowPluginVersion}, and Eigent config sentinels compiled correctly.\n`
    );
  }
} finally {
  fs.rmSync(fixturePath, { force: true });
  fs.rmSync(outputPath, { force: true });
}