Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion src/atrule/tailwind-apply.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@ import { tokenTypes } from "@eslint/css-tree";

export default {
parse: {

/**
* @this {ParserContext}
*/
prelude: function() {
const children = this.createList();
let hasImportant = false;

while (this.tokenType === tokenTypes.Ident) {

Expand All @@ -42,6 +42,17 @@ export default {
this.skipSC();
}

// Check for !important at the end
if (this.tokenType === tokenTypes.Delim && this.source.charCodeAt(this.tokenStart) === 33) { // 33 is '!'
this.next(); // consume !
this.skipSC();

if (this.tokenType === tokenTypes.Ident && this.source.slice(this.tokenStart, this.tokenEnd) === "important") {
this.next(); // consume important
hasImportant = true;
}
}

return children;
},
block: null
Expand Down
1 change: 1 addition & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@
/* @ts-self-types="./index.d.ts" */
export { tailwind4 } from "./tailwind4.js";
export { tailwind3 } from "./tailwind3.js";
export { addImportantToApplyAtrules, forkWithApplyImportant } from "./utils/apply-important.js";
2 changes: 1 addition & 1 deletion src/tailwind3.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export const tailwind3 = {
},
atrules: {
apply: {
prelude: "<tw-apply-ident>+",
prelude: "<tw-apply-ident>+ [ '!' important ]?",
},
tailwind: {
prelude: "base | components | utilities | variants",
Expand Down
2 changes: 1 addition & 1 deletion src/tailwind4.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import { themeTypes } from "./types/theme-types.js";
export const tailwind4 = {
atrules: {
apply: {
prelude: "<ident>+",
prelude: "<ident>+ [ '!' important ]?",
},
config: {
prelude: "<string>",
Expand Down
88 changes: 88 additions & 0 deletions src/utils/apply-important.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/**
* @fileoverview Utility functions for handling !important in @apply directives
* @author Nicholas C. Zakas
*/

//-----------------------------------------------------------------------------
// Imports
//-----------------------------------------------------------------------------

import { fork as baseFork } from "@eslint/css-tree";

//-----------------------------------------------------------------------------
// Helper Functions
//-----------------------------------------------------------------------------

/**
* Walk through AST and add important property to @apply atrules that need it
* @param {Object} ast - CSS AST
* @param {string} originalSource - Original CSS source code
* @returns {Object} Modified AST
*/
export function addImportantToApplyAtrules(ast, originalSource) {

function walk(/** @type {any} */ node) {
if (!node || typeof node !== 'object') {
return;
}

// Check if this is an @apply atrule
if (node.type === 'Atrule' && node.name === 'apply' && node.loc) {
// Extract the original source for this atrule to see if it had !important
const start = node.loc.start.offset;
const end = node.loc.end.offset;
const atruleSource = originalSource.slice(start, end);

// If the original source contains !important, add the flag
if (atruleSource.includes('!important')) {
/** @type {any} */ (node).important = true;
}
}

// Recursively walk children
if (node.children) {
if (Array.isArray(node.children)) {
node.children.forEach(/** @param {any} child */ (child) => walk(child));
} else if (node.children.forEach) {
// Handle CSS Tree List objects
node.children.forEach(/** @param {any} child */ (child) => walk(child));
}
}

if (node.prelude) {
walk(node.prelude);
}

if (node.block) {
walk(node.block);
}
}

walk(ast);
return ast;
}

/**
* Create a CSS Tree fork with automatic !important support for @apply
* @param {Object} config - CSS Tree configuration
* @returns {Object} Enhanced CSS Tree fork
*/
export function forkWithApplyImportant(config) {
const parser = baseFork(config);
const originalParse = parser.parse;

parser.parse = function(source, options) {
// Parse with positions enabled to get offset information
const parseOptions = { ...options, positions: true };
const result = originalParse.call(this, source, parseOptions);

// Apply !important post-processing for Tailwind configs
if (config && config.atrule && config.atrule.apply) {
return addImportantToApplyAtrules(result, source);
}

return result;
};

return parser;
}
Loading