Skip to content

Commit dfeeaec

Browse files
johnjenkinsJohn Jenkins
andauthored
fix(css): strip line breaks from final template literal (#6517)
Co-authored-by: John Jenkins <john.jenkins@nanoporetech.com>
1 parent cdcd873 commit dfeeaec

2 files changed

Lines changed: 98 additions & 5 deletions

File tree

src/compiler/style/css-to-esm.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -165,15 +165,15 @@ const generateTransformCssToEsm = (
165165
): d.TransformCssToEsmOutput => {
166166
const s = new MagicString('');
167167

168-
// Double existing backslashes; so `\\f101` parses to `\f101` at runtime
169-
// handles plain css string or js variable input (i.e from testing)
168+
// Replace literal newlines/tabs/returns with spaces to avoid them becoming escape sequences
169+
// Then handle other special characters and escape backslashes last
170170
results.styleText = results.styleText
171+
.replace(/\n/g, ' ')
172+
.replace(/\r/g, ' ')
173+
.replace(/\t/g, ' ')
171174
.replace(/`/g, '\\`')
172175
.replace(/\u000c/g, '\\f')
173176
.replace(/\u0008/g, '\\b')
174-
.replace(/\n/g, '\\n')
175-
.replace(/\r/g, '\\r')
176-
.replace(/\t/g, '\\t')
177177
.replace(/\u000b/g, '\\v')
178178
.replace(/\0/g, '\\0')
179179
.replace(/\\/g, '\\\\');

src/compiler/style/test/css-to-esm.spec.ts

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,4 +357,97 @@ describe('transformCssToEsm', () => {
357357
expect(result.output).toContain('"\\\\f101"');
358358
expect(result.output).toContain("'\\\\f102'");
359359
});
360+
361+
it('replaces newlines with spaces to avoid escape sequence issues', () => {
362+
const result = transformCssToEsmSync({
363+
input: '.my-component {\n --theme-primary-color: #2c3e50;\n display: block;\n}',
364+
file: '/test.css',
365+
mode: 'md',
366+
module: 'esm',
367+
tags: [],
368+
addTagTransformers: false,
369+
encapsulation: undefined,
370+
docs: false,
371+
sourceMap: false,
372+
styleImportData: undefined,
373+
});
374+
375+
// Should not contain literal \n escape sequences
376+
expect(result.output).not.toContain('\\n');
377+
// Should contain spaces instead
378+
expect(result.output).toContain('.my-component {');
379+
expect(result.output).toContain('--theme-primary-color');
380+
});
381+
382+
it('handles multiline color-mix CSS correctly', () => {
383+
const input = `.my-component {
384+
--theme-primary-color: #2c3e50;
385+
--theme-primary-opacity: 10%;
386+
--theme-secondary-color: #0a0a0a;
387+
--theme-secondary-opacity: 20%;
388+
389+
display: block;
390+
color: color-mix(in srgb, var(--theme-primary-color) var(--theme-primary-opacity),
391+
var(--theme-secondary-color) var(--theme-secondary-opacity));
392+
}`;
393+
394+
const result = transformCssToEsmSync({
395+
input,
396+
file: '/test.css',
397+
mode: 'md',
398+
module: 'esm',
399+
tags: [],
400+
addTagTransformers: false,
401+
encapsulation: undefined,
402+
docs: false,
403+
sourceMap: false,
404+
styleImportData: undefined,
405+
});
406+
407+
// Should not contain \n escape sequences
408+
expect(result.output).not.toContain('\\n');
409+
// Should contain the color-mix function properly
410+
expect(result.output).toContain('color-mix');
411+
expect(result.output).toContain('in srgb');
412+
});
413+
414+
it('replaces tabs and carriage returns with spaces', () => {
415+
const result = transformCssToEsmSync({
416+
input: '.my-class\t{\tcolor:\tred;\r\n}',
417+
file: '/test.css',
418+
mode: 'md',
419+
module: 'esm',
420+
tags: [],
421+
addTagTransformers: false,
422+
encapsulation: undefined,
423+
docs: false,
424+
sourceMap: false,
425+
styleImportData: undefined,
426+
});
427+
428+
// Should not contain escape sequences for tabs or newlines
429+
expect(result.output).not.toContain('\\t');
430+
expect(result.output).not.toContain('\\r');
431+
expect(result.output).not.toContain('\\n');
432+
expect(result.output).toContain('.my-class');
433+
expect(result.output).toContain('color');
434+
});
435+
436+
it('escapes backticks in CSS content', () => {
437+
const result = transformCssToEsmSync({
438+
input: '.my-class::before { content: "`"; }',
439+
file: '/test.css',
440+
mode: 'md',
441+
module: 'esm',
442+
tags: [],
443+
addTagTransformers: false,
444+
encapsulation: undefined,
445+
docs: false,
446+
sourceMap: false,
447+
styleImportData: undefined,
448+
});
449+
450+
// Should escape backticks to avoid breaking template literal
451+
expect(result.output).toContain('\\`');
452+
});
360453
});

0 commit comments

Comments
 (0)