'use strict';
const fs = require('node:fs');
const path = require('node:path');
const KEYS = [
'colors',
'spacing',
'fontSize',
'fontWeight',
'fontFamily',
'borderRadius',
];
function emitError(code, message) {
const payload = JSON.stringify({ plumbTailwindError: { code, message } });
process.stdout.write(payload + '\n');
process.exit(2);
}
function tryRegisterTsLoader(configDir) {
const candidates = ['tsx/cjs', 'ts-node/register/transpile-only', 'esbuild-register'];
for (const candidate of candidates) {
try {
const resolved = require.resolve(candidate, { paths: [configDir] });
require(resolved);
return candidate;
} catch (_err) {
}
}
return null;
}
function loadUserConfig(configPath) {
const ext = path.extname(configPath).toLowerCase();
const isTs = ext === '.ts' || ext === '.mts' || ext === '.cts';
if (isTs) {
const loader = tryRegisterTsLoader(path.dirname(configPath));
if (!loader) {
emitError(
'TS_LOADER_MISSING',
'No TypeScript loader found. Install one of `tsx`, `ts-node`, or `esbuild-register` in your project, or convert the config to .js/.mjs/.cjs.',
);
}
}
let mod;
try {
mod = require(configPath);
} catch (err) {
emitError('CONFIG_REQUIRE_FAILED', `Failed to load Tailwind config: ${err && err.message ? err.message : String(err)}`);
}
if (mod && typeof mod === 'object' && 'default' in mod && Object.keys(mod).length === 1) {
return mod.default;
}
return mod;
}
function tryResolveWithTailwind(userConfig, configDir) {
try {
const resolved = require.resolve('tailwindcss/resolveConfig', {
paths: [configDir],
});
const resolveConfig = require(resolved);
const result = resolveConfig(userConfig);
return result && result.theme ? result.theme : null;
} catch (_err) {
return null;
}
}
function fallbackResolveTheme(userConfig) {
const theme = (userConfig && userConfig.theme) || {};
const extend = theme.extend || {};
const out = {};
for (const key of KEYS) {
const base = theme[key];
const ext = extend[key];
if (base === undefined && ext === undefined) continue;
if (typeof base !== 'object' || base === null) {
out[key] = ext === undefined ? base : ext;
continue;
}
out[key] = Object.assign({}, base, ext || {});
}
return out;
}
function pickTheme(theme) {
const out = {};
for (const key of KEYS) {
if (theme && Object.prototype.hasOwnProperty.call(theme, key)) {
out[key] = theme[key];
}
}
return out;
}
function main() {
let configPath = process.argv[1];
if (configPath === '--') {
configPath = process.argv[2];
}
if (!configPath) {
emitError('USAGE', 'Usage: loader.js <tailwind-config-path>');
}
if (!fs.existsSync(configPath)) {
emitError('CONFIG_NOT_FOUND', `Tailwind config not found: ${configPath}`);
}
const absolute = path.resolve(configPath);
const configDir = path.dirname(absolute);
const userConfig = loadUserConfig(absolute);
if (!userConfig || typeof userConfig !== 'object') {
emitError('CONFIG_NOT_OBJECT', 'Tailwind config did not export an object.');
}
const resolved = tryResolveWithTailwind(userConfig, configDir);
const theme = resolved !== null ? resolved : fallbackResolveTheme(userConfig);
const picked = pickTheme(theme);
process.stdout.write(JSON.stringify(picked));
process.stdout.write('\n');
}
main();