Skip to content
Merged
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
1 change: 0 additions & 1 deletion docs/class-regex.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,6 @@ These patterns work fine for IDE IntelliSense (which only reads, never writes) b
Even if a pattern passes the pattern-level check, each captured string is validated before sorting:

- Captures containing `<`, `>`, `{`, `}` are skipped (HTML/Latte structural characters)
- Captures containing `,` are skipped (inter-token separators, not class names)

This prevents sorting of accidentally captured file structure.

Expand Down
5 changes: 2 additions & 3 deletions src/class-regex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,10 @@ function warnUnsafe(source: string): void {
/**
* Runtime safety: skip capture groups that contain structural characters
* which indicate the regex matched beyond class attribute boundaries.
* Valid CSS class strings never contain < > { } , (commas indicate
* inter-token separators were captured instead of class names).
* Valid CSS class strings never contain < > { }.
*/
function isSafeCapture(captured: string): boolean {
return !/[<>{},]/.test(captured)
return !/[<>{}]/.test(captured)
}

// Re-export for testing
Expand Down
7 changes: 7 additions & 0 deletions tests/class-regex.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,13 @@ describe('capture safety (runtime)', () => {
expect(result).toBe(code)
})

it('allows captures with commas inside arbitrary values', () => {
const code = "class: 'flex bg-[rgb(255,0,0)] mt-4'"
const patterns = parseClassRegexPatterns(JSON.stringify(["class:\\s*'([^']*)'"]))
const result = applyClassRegex(code, patterns, sortFn)
expect(result).not.toBe(code)
})

it('skips capture groups containing Latte brackets', () => {
const code = 'class="flex {$var} mt-2"'
const patterns = parseClassRegexPatterns(JSON.stringify(['"([^"]*)"']))
Expand Down
16 changes: 16 additions & 0 deletions tests/nclass.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,22 @@ describe('parseNClass', () => {
expect(r.tokens).toHaveLength(2)
})

it('handles bare arbitrary value with commas as single token', () => {
const r = parseNClass('mt-4, bg-[rgb(255,0,0)], flex')
expect(r.tokens).toHaveLength(3)
expect(r.tokens[0].content).toBe('mt-4')
expect(r.tokens[1].content).toBe('bg-[rgb(255,0,0)]')
expect(r.tokens[2].content).toBe('flex')
})

it('handles quoted arbitrary value with commas as single token', () => {
const r = parseNClass("'mt-4', 'bg-[rgb(255,0,0)]', 'flex'")
expect(r.tokens).toHaveLength(3)
expect(r.tokens[1].content).toBe("'bg-[rgb(255,0,0)]'")
expect(r.tokens[1].sortable).toBe(true)
expect(r.tokens[1].sortKey).toBe('bg-[rgb(255,0,0)]')
})

it('skips ?-> (null-safe)', () => {
const r = parseNClass('$obj?->method()')
expect(r.tokens).toHaveLength(1)
Expand Down