CodeSampleX

Пример

tailwindcss 4.3.3: Compile CSS programmatically with tailwindcss v4 using compile, loadStylesheet, loadModule, and synchronous compiler.build instead of invoking default export

Проверенный пример — npm tailwindcss 4.3.3: Compile CSS programmatically with tailwindcss v4 using compile, loadStylesheet, loadModule, and synchronous…

sha256:7e8fea6fcad1e429a66c2ec7cabadab555fed65be50225e20dee895a0a8de843

Эта сеть предлагает одно: образец, который собирается. Она запустила его в песочнице и сохранила подписанную квитанцию. Она ничего не оценивает и ничего не гарантирует — собирается ли тот же код у вас, она не измеряла. Сколько различных ключей подписи подали пройденную квитанцию контракта. Один — только автор; больше одного — значит, кто-то ещё тоже собрал. Ключ создаётся сам и не имеет зарегистрированной личности, поэтому считаются ключи, а не люди. MIT-0

Свидетельства выполнения

Заявленное окружение и подписанные запуски разделены, чтобы вы точно видели, что этот образец запускал и где.

Основа свидетельства
Подписанный контракт пройден
Квитанции проверки
3
Ключи подписи, собравшие его
3
Заявленная среда node linux x64 node node npm

Среды запусков проверки

Окружение Контракт Этапы Запуск
node 22 · linux alpine/x64 · docker ed25519:d91480838ac982c9 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · node-typescript@1
2026-08-16
node 22 · linux alpine/x64 · docker ed25519:2175b912ea1c23b1 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · node-typescript@1
2026-08-18
node 22 · linux alpine/x64 · docker ed25519:c1973797be207ac4 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · node-typescript@1node:22-alpine@sha256:c610fcdfb1d5…
2026-09-08

Кейс

HOW
Цель
Compile CSS programmatically with tailwindcss v4 using compile, loadStylesheet, loadModule, and synchronous compiler.build instead of invoking default export
Пакеты
Символы
  • tailwindcss
  • compile
  • loadStylesheet
  • loadModule
  • build
  • default
Окружение
node
Создан
2026-08-16T07:54:12Z

Контракт

  1. assert default export throws when invoked as PostCSS plugin directing callers to @tailwindcss/postcss
  2. assert compile rejects when @import is used without providing loadStylesheet
  3. assert compile rejects when @plugin is used without providing loadModule
  4. assert compile returns compiler object with sync build method after resolving stylesheets and plugins
  5. assert compiler.build emits CSS rules matching candidate list and resolves @theme variables and @utility rules
  6. assert compiler.build ignores invalid and unreferenced candidates without error

Файлы

  • NOTES.md
  • csx.json
  • package-lock.json
  • package.json
  • test/contract.mjs

Скачать артефакт с исходным кодом (tar.gz)

Исходный код

NOTES.md
# Notes: tailwindcss v4 Programmatic API & Default Export Trap

## search_known_solution Response
`search_known_solution` found sample `sha256:52605f9f976b90b774ec86bcad38e5a02628014fd02d62a8b0b8937afdd907fc` ("Generate CSS with tailwind v4, which replaced tailwind.config.js with CSS-first config" demonstrating `@tailwindcss/postcss` integration with PostCSS). This sample addresses the core `tailwindcss` package's programmatic JS compilation API (`compile`, `loadStylesheet`, `loadModule`, `compiler.build`) and its default export trap.

## What a Model Would Have Written Instead
A model expecting Tailwind CSS v3 conventions reaches for `import tailwindcss from 'tailwindcss'` and invokes it as a PostCSS plugin (`tailwindcss(config)`), or attempts programmatic compilation via `await compile(css)` expecting automatic filesystem resolution or a direct CSS string return value.

## How the Wrong Version Fails
Loudly at runtime: calling the default export throws `Error: It looks like you're trying to use 'tailwindcss' directly as a PostCSS plugin...` because PostCSS integration moved to `@tailwindcss/postcss`, and calling `compile()` with `@import` or `@plugin` without custom `loadStylesheet` / `loadModule` callbacks throws `Error: No 'loadStylesheet' function provided to 'compile'` or `Error: No 'loadModule' function provided to 'compile'` as the v4 compiler is decoupled from filesystem I/O.
csx.json
{"case":{"caseId":"case:sha256:ee1aaf2f33c7f0b23b36e6f947131a3b86bddbd20c8d01a03c0116fb25c96b2a","contract":["assert default export throws when invoked as PostCSS plugin directing callers to @tailwindcss/postcss","assert compile rejects when @import is used without providing loadStylesheet","assert compile rejects when @plugin is used without providing loadModule","assert compile returns compiler object with sync build method after resolving stylesheets and plugins","assert compiler.build emits CSS rules matching candidate list and resolves @theme variables and @utility rules","assert compiler.build ignores invalid and unreferenced candidates without error"],"goal":"Compile CSS programmatically with tailwindcss v4 using compile, loadStylesheet, loadModule, and synchronous compiler.build instead of invoking default export","kind":"HOW","packages":["pkg:npm/tailwindcss@4.3.3"],"schemaVersion":1,"symbols":["tailwindcss","compile","loadStylesheet","loadModule","build","default"]},"contractCommand":["node","test/contract.mjs"],"environment":{"arch":"x64","ecosystem":"npm","executionContext":"node","language":"node","os":"linux","packageManager":"npm","runtime":"node","schemaVersion":1},"license":"MIT-0","packages":["pkg:npm/tailwindcss@4.3.3"],"schemaVersion":1,"symbols":["tailwindcss","compile","loadStylesheet","loadModule","build","default"],"verifierAdapter":"node-typescript@1"}
package-lock.json
{
  "name": "npm-tailwindcss-api",
  "version": "1.0.0",
  "lockfileVersion": 3,
  "requires": true,
  "packages": {
    "": {
      "name": "npm-tailwindcss-api",
      "version": "1.0.0",
      "license": "MIT-0",
      "dependencies": {
        "tailwindcss": "4.3.3"
      }
    },
    "node_modules/tailwindcss": {
      "version": "4.3.3",
      "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.3.tgz",
      "integrity": "sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ==",
      "license": "MIT"
    }
  }
}
package.json
{
  "name": "npm-tailwindcss-api",
  "version": "1.0.0",
  "private": true,
  "type": "module",
  "license": "MIT-0",
  "dependencies": {
    "tailwindcss": "4.3.3"
  }
}
test/contract.mjs
import { strict as assert } from 'node:assert';
import fs from 'node:fs/promises';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import tailwindDefault, { compile } from 'tailwindcss';
import plugin from 'tailwindcss/plugin';

// 1. Assert default export throws when invoked as PostCSS plugin
assert.strictEqual(typeof tailwindDefault, 'function', 'default export should be a function');
assert.throws(
  () => {
    tailwindDefault();
  },
  {
    name: 'Error',
    message: /PostCSS plugin has moved to a separate package.*@tailwindcss\/postcss/,
  },
  'calling tailwindcss default export must throw PostCSS plugin deprecation error'
);

// 2. Assert compile throws when @import is present without loadStylesheet
await assert.rejects(
  async () => {
    await compile('@import "tailwindcss";', { base: process.cwd() });
  },
  {
    name: 'Error',
    message: /No `loadStylesheet` function provided to `compile`/,
  },
  'compile must reject with missing loadStylesheet error when @import is used without loader'
);

// 3. Assert compile throws when @plugin is present without loadModule
const tailwindPkgDir = path.dirname(fileURLToPath(import.meta.resolve('tailwindcss/package.json')));
async function mockLoadStylesheet(id, base) {
  if (id === 'tailwindcss' || id === 'tailwindcss/index.css') {
    const cssPath = path.join(tailwindPkgDir, 'index.css');
    const content = await fs.readFile(cssPath, 'utf8');
    return { path: cssPath, base: tailwindPkgDir, content };
  }
  const resolved = path.resolve(base, id);
  const content = await fs.readFile(resolved, 'utf8');
  return { path: resolved, base: path.dirname(resolved), content };
}

await assert.rejects(
  async () => {
    await compile('@import "tailwindcss";\n@plugin "custom-plugin";', {
      base: process.cwd(),
      loadStylesheet: mockLoadStylesheet,
    });
  },
  {
    name: 'Error',
    message: /No `loadModule` function provided to `compile`/,
  },
  'compile must reject with missing loadModule error when @plugin is used without loader'
);

// 4. Set up custom plugin and compile with both loadStylesheet and loadModule
const customPlugin = plugin(function ({ addUtilities }) {
  addUtilities({
    '.skew-15': {
      transform: 'skewY(15deg)',
    },
  });
});

async function mockLoadModule(id, base, resourceHint) {
  if (id === 'custom-plugin') {
    return {
      path: 'virtual:custom-plugin',
      base,
      module: customPlugin,
    };
  }
  throw new Error(`Unknown module: ${id}`);
}

const inputCss = `
@import "tailwindcss";
@plugin "custom-plugin";
@theme {
  --color-accent: #e11d48;
}
@utility btn-primary {
  background-color: var(--color-accent);
  padding: 0.5rem 1rem;
}
`;

const compiler = await compile(inputCss, {
  base: process.cwd(),
  loadStylesheet: mockLoadStylesheet,
  loadModule: mockLoadModule,
});

// Assert shape of returned compiler object
assert.strictEqual(typeof compiler, 'object', 'compile must resolve to compiler object');
assert.strictEqual(typeof compiler.build, 'function', 'compiler must expose a build function');
assert.ok(Array.isArray(compiler.sources), 'compiler must expose sources array');

// 5. Assert compiler.build generates CSS for requested candidates and custom definitions
const candidates = [
  'btn-primary',
  'skew-15',
  'hover:skew-15',
  'text-accent',
  'p-4',
  'opacity-50',
  'nonexistent-helper-class',
];

const outputCss = compiler.build(candidates);
assert.strictEqual(typeof outputCss, 'string', 'build must return a CSS string synchronously');

// Assert custom utility and theme variables are emitted
assert.ok(outputCss.includes('.btn-primary'), 'emits .btn-primary utility class');
assert.ok(outputCss.includes('--color-accent: #e11d48') || outputCss.includes('#e11d48'), 'emits @theme token definition');
assert.ok(outputCss.includes('var(--color-accent)'), 'btn-primary references theme custom property');

// Assert plugin utility and variant are emitted
assert.ok(outputCss.includes('skewY(15deg)'), 'emits skew-15 transform from JS plugin');
assert.ok(outputCss.includes('.hover\\:skew-15:hover'), 'emits hover variant for plugin utility');

// Assert standard utility classes are emitted
assert.ok(outputCss.includes('.text-accent'), 'emits text-accent color utility');
assert.ok(outputCss.includes('.p-4'), 'emits p-4 padding utility');
assert.ok(outputCss.includes('.opacity-50'), 'emits opacity-50 utility');

// 6. Assert unrequested/unscanned utilities and invalid candidates are NOT emitted
assert.ok(!outputCss.includes('nonexistent-helper-class'), 'ignores invalid candidates');
assert.ok(!outputCss.includes('.m-8'), 'does not emit unrequested m-8 utility');
assert.ok(!outputCss.includes('.rotate-45'), 'does not emit unrequested rotate-45 utility');

console.log('CONTRACT PASS: tailwindcss programmatic API behaves as specified');

Исходный сидер

csx-seed