@@ -891,50 +891,7 @@ export async function startSSEListener(ctx: Context) {
891891 }
892892}
893893
894- /**
895- * Validates if a string contains valid CSS syntax
896- * @param cssString - The CSS string to validate
897- * @returns true if valid CSS, false otherwise
898- */
899- export function isValidCSS ( cssString : string ) : boolean {
900- if ( ! cssString || typeof cssString !== 'string' || cssString . trim ( ) . length === 0 ) {
901- return false ;
902- }
903-
904- const trimmed = cssString . trim ( ) ;
905-
906- // Basic CSS validation patterns
907- // Check for balanced braces
908- const openBraces = ( trimmed . match ( / \{ / g) || [ ] ) . length ;
909- const closeBraces = ( trimmed . match ( / \} / g) || [ ] ) . length ;
910-
911- if ( openBraces !== closeBraces ) {
912- return false ;
913- }
914-
915- // Check for basic CSS structure (selector { property: value; })
916- // Allow comments /* */ and media queries
917- const cssPattern = / ^ [ \s \S ] * [ \{ \} ] [ \s \S ] * $ / ;
918-
919- // Must contain at least one CSS rule or be empty
920- if ( trimmed . length > 0 && ! cssPattern . test ( trimmed ) ) {
921- // Allow single-line rules without newlines
922- const singleRulePattern = / ^ [ ^ { ] + \{ [ ^ } ] + \} $ / ;
923- if ( ! singleRulePattern . test ( trimmed ) ) {
924- return false ;
925- }
926- }
927894
928- return true ;
929- }
930-
931- /**
932- * Resolves customCSS from either a file path or inline CSS string
933- * @param cssValue - The CSS value from config (file path or inline CSS)
934- * @param configPath - The path to the config file (for resolving relative paths)
935- * @param logger - Logger instance for debug messages
936- * @returns Resolved CSS string or throws error if invalid
937- */
938895export function resolveCustomCSS ( cssValue : string , configPath : string , logger : any ) : string {
939896 if ( ! cssValue || typeof cssValue !== 'string' ) {
940897 throw new Error ( 'customCSS must be a non-empty string' ) ;
@@ -948,11 +905,13 @@ export function resolveCustomCSS(cssValue: string, configPath: string, logger: a
948905 // Check if it looks like a file path
949906 const path = require ( 'path' ) ;
950907 const isLikelyFilePath =
951- trimmed . endsWith ( '.css' ) ||
952- trimmed . startsWith ( './' ) ||
953- trimmed . startsWith ( '../' ) ||
954- trimmed . startsWith ( '/' ) ||
955- path . isAbsolute ( trimmed ) ;
908+ trimmed . includes ( '.' ) && (
909+ trimmed . startsWith ( './' ) ||
910+ trimmed . startsWith ( '../' ) ||
911+ trimmed . startsWith ( '/' ) ||
912+ path . isAbsolute ( trimmed ) ||
913+ / \. ( c s s | j s o n | j s | t x t | h t m l ) $ / i. test ( trimmed )
914+ ) ;
956915
957916 if ( isLikelyFilePath ) {
958917 logger . debug ( `customCSS appears to be a file path: ${ trimmed } ` ) ;
@@ -962,6 +921,10 @@ export function resolveCustomCSS(cssValue: string, configPath: string, logger: a
962921 if ( ext && ext !== '.css' ) {
963922 throw new Error ( `Invalid customCSS file type: ${ ext } . Only .css files are supported.` ) ;
964923 }
924+ // If no extension at all, also reject
925+ if ( ! ext ) {
926+ throw new Error ( 'Invalid file provided in customCSS. Expected .css file.' ) ;
927+ }
965928
966929 // Resolve the file path
967930 const baseDir = path . dirname ( configPath ) ;
@@ -971,7 +934,6 @@ export function resolveCustomCSS(cssValue: string, configPath: string, logger: a
971934
972935 logger . debug ( `Resolved customCSS file path: ${ resolvedPath } ` ) ;
973936
974- // Check if file exists
975937 if ( ! fs . existsSync ( resolvedPath ) ) {
976938 throw new Error ( `customCSS file not found: ${ resolvedPath } ` ) ;
977939 }
@@ -982,7 +944,6 @@ export function resolveCustomCSS(cssValue: string, configPath: string, logger: a
982944 throw new Error ( `customCSS path is not a file: ${ resolvedPath } ` ) ;
983945 }
984946
985- // Read the file
986947 try {
987948 const cssContent = fs . readFileSync ( resolvedPath , 'utf-8' ) ;
988949 logger . debug ( `Read ${ cssContent . length } characters from customCSS file` ) ;
@@ -995,18 +956,12 @@ export function resolveCustomCSS(cssValue: string, configPath: string, logger: a
995956 throw new Error ( `Failed to read customCSS file: ${ error . message } ` ) ;
996957 }
997958 } else {
998- // Treat as inline CSS
999959 logger . debug ( 'customCSS appears to be inline CSS' ) ;
1000960 return trimmed ;
1001961 }
1002962}
1003963
1004964
1005- /**
1006- * Parse CSS content and extract selectors with their rules
1007- * @param cssContent - The CSS content to parse
1008- * @returns Array of parsed CSS rules with selectors
1009- */
1010965export function parseCSSFile ( cssContent : string ) : Array < {
1011966 selector : string ;
1012967 declarations : Array < { property : string ; value : string ; important : boolean } > ;
@@ -1022,6 +977,13 @@ export function parseCSSFile(cssContent: string): Array<{
1022977 const ast = postcss . parse ( cssContent ) ;
1023978
1024979 ast . walkRules ( ( rule : any ) => {
980+
981+ // Skip rules inside @keyframes , @media, and other at-rules
982+ // by checking if the parent is an AtRule
983+ if ( rule . parent && rule . parent . type === 'atrule' ) {
984+ return ; // Skip this rule
985+ }
986+
1025987 const declarations : Array < { property : string ; value : string ; important : boolean } > = [ ] ;
1026988
1027989 rule . walkDecls ( ( decl : any ) => {
0 commit comments