Skip to content
Open
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
14 changes: 14 additions & 0 deletions packages/cli/src/linter/tailwind/v4/handler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,20 @@ describe('TailwindV4EmitterHandler', () => {
if (!result.success) throw new Error('Expected success');
expect(result.data.theme.fontFamily?.['fancy']).toBe('"Fancy \\"Font\\""');
});

it('escapes line terminators in font-family values', () => {
const state = buildState({
typography: {
multi: { fontFamily: 'Evil\nFamily', fontSize: '16px' },
},
});
const result = emitter.execute(state);
if (!result.success) throw new Error('Expected success');
const value = result.data.theme.fontFamily?.['multi'];
// A raw newline must not survive into the emitted CSS string.
expect(value).not.toContain('\n');
expect(value).toBe('"Evil\\a Family"');
});
});

describe('dimensions mapping', () => {
Expand Down
8 changes: 7 additions & 1 deletion packages/cli/src/linter/tailwind/v4/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,13 @@ function dimToString(dim: ResolvedDimension): string {
/**
* Wrap a string value in double quotes, escaping embedded `\` and `"`.
* Produces a CSS-safe string literal suitable for font-family values.
* Line terminators (newline, carriage return, form feed) are illegal raw inside
* a CSS string and could break the value out of the quoted token, so they are
* emitted as CSS hex escapes (e.g. `\a `).
*/
function cssStringLiteral(value: string): string {
return `"${value.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
return `"${value
.replace(/\\/g, '\\\\')
.replace(/"/g, '\\"')
.replace(/[\n\r\f]/g, (c) => `\\${c.charCodeAt(0).toString(16)} `)}"`;
}