UNPKG

@unizap/unicss

Version:

Unicss: Build sleek interfaces straight from your markup. Fast, modern, utility-first CSS framework for rapid UI development.

180 lines (157 loc) 5.4 kB
const generateCSS = require('./dist/index.js'); const glob = require('glob'); /** * PostCSS plugin for UniCSS * Processes @unicss directives and @apply utility classes * * Usage in postcss.config.js: * module.exports = { * plugins: { * '@unizap/unicss': { skipBase: false }, * autoprefixer: {} * } * } * * Usage in CSS: * @unicss base; * @unicss utilities; * * Or simply: * @unicss; * * @apply usage: * .card { @apply shadow-md p-5 rounded-lg; } */ module.exports = (opts = {}) => { const skipBase = opts.skipBase || false; return { postcssPlugin: 'unicss', Once(root, { result }) { const postcss = require('postcss'); // Add all source files as dependencies so PostCSS/Webpack/Vite watches them const sourceFiles = getAllSourceFiles(); sourceFiles.forEach(file => { result.messages.push({ type: 'dependency', plugin: 'unicss', file: file, parent: result.opts.from }); }); const hasApply = root.toString().includes('@apply'); let hasUnicssDirective = false; let baseDirective = null; let utilitiesDirective = null; let fullDirective = null; // Find @unicss directives root.walkAtRules('unicss', (rule) => { hasUnicssDirective = true; const param = rule.params.trim(); if (param === 'base') { baseDirective = rule; } else if (param === 'utilities') { utilitiesDirective = rule; } else if (param === '' || param === 'all') { fullDirective = rule; } else { rule.warn(result, `Unknown @unicss directive: "${param}". Use "base", "utilities", or leave empty.`); } }); if (!hasUnicssDirective && !hasApply) return; // Build a processor once — reuses the same config machinery as generateCSS // but without scanning files or producing output, so it's instant. const processor = generateCSS.createProcessor({ skipBase }); // Process @apply rules: replace each with resolved CSS declarations root.walkAtRules('apply', (atRule) => { const classNames = atRule.params.trim().split(/\s+/).filter(Boolean); const decls = []; const unresolved = []; for (const cls of classNames) { // Strip variant prefixes — only base declarations are inlined const base = cls.replace(/^(?:(?:dark:)?(?:2xl|xl|lg|md|sm):)?(?:[a-z][a-z0-9-]*:)*/, ''); const isImportant = base.startsWith('!'); const clean = isImportant ? base.slice(1) : base; const body = processor.buildRuleForClassWithoutSelector(clean, isImportant); if (body) { body.split(';') .map(d => d.trim()) .filter(Boolean) .forEach(d => { const colonIdx = d.indexOf(':'); if (colonIdx > 0) { decls.push(postcss.decl({ prop: d.slice(0, colonIdx).trim(), value: d.slice(colonIdx + 1).trim(), })); } }); } else { unresolved.push(cls); } } if (unresolved.length) { atRule.parent.insertBefore( atRule, postcss.comment({ text: ` @apply: unresolved classes: ${unresolved.join(' ')} ` }) ); } atRule.replaceWith(...decls); }); if (!hasUnicssDirective) return; try { const generatedCSS = generateCSS({ skipBase }); const generatedRoot = postcss.parse(generatedCSS); if (fullDirective) { fullDirective.replaceWith(generatedRoot.nodes); } else { if (baseDirective) { const baseRoot = postcss.parse(extractBaseStyles(generatedCSS)); baseDirective.replaceWith(baseRoot.nodes); } if (utilitiesDirective) { const utilitiesRoot = postcss.parse(extractUtilities(generatedCSS)); utilitiesDirective.replaceWith(utilitiesRoot.nodes); } } } catch (error) { throw root.error(`UniCSS generation failed: ${error.message}`); } } }; }; module.exports.postcss = true; /** * Get all source files that should trigger CSS regeneration */ function getAllSourceFiles() { const cwd = process.cwd(); const extensions = ['html', 'js', 'jsx', 'ts', 'tsx', 'vue', 'svelte']; const files = []; extensions.forEach(ext => { const matches = glob.sync(`**/*.${ext}`, { cwd: cwd, absolute: true, ignore: ['**/node_modules/**', '**/dist/**', '**/build/**', '**/.next/**'] }); files.push(...matches); }); return files; } /** * Extract base styles from generated CSS * For now, we just return the full CSS when base is requested * TODO: Implement intelligent splitting between base and utilities */ function extractBaseStyles(css) { // Return full CSS for now - users can use @unicss without params for everything return css; } /** * Extract utility classes from generated CSS * For now, we just return the full CSS when utilities is requested * TODO: Implement intelligent splitting between base and utilities */ function extractUtilities(css) { // Return full CSS for now - users can use @unicss without params for everything return css; }