From 0bc448b53bd28b13f2eacde0227f0943fca2b681 Mon Sep 17 00:00:00 2001 From: Wahid Rizka Fathurrohman Date: Tue, 6 Oct 2026 11:41:21 +0700 Subject: [PATCH] Remove backslash line continuations from the CSS output A backslash before a new line continues the line in SugarSS, but the parser kept it in selectors, at-rule params and values. CSS has no such escape, so browsers dropped those rules and declarations. Keep the backslash only in the SugarSS raw, so stringify still gives the input. --- parser.js | 15 ++++ test/cases/backslash.css | 14 ++++ test/cases/backslash.json | 148 ++++++++++++++++++++++++++++++++++++++ test/cases/backslash.sss | 11 +++ 4 files changed, 188 insertions(+) create mode 100644 test/cases/backslash.css create mode 100644 test/cases/backslash.json create mode 100644 test/cases/backslash.sss diff --git a/parser.js b/parser.js index f1295ca..900ec46 100644 --- a/parser.js +++ b/parser.js @@ -1,5 +1,15 @@ let { AtRule, Comment, Declaration, Root, Rule } = require('postcss') +// Backslash before a new line continues the line in SugarSS. CSS has no such +// escape, so browsers reject the value if the backslash stays in the output. +function isLineContinuation(token) { + return ( + token[0] === 'word' && + token[1][0] === '\\' && + /^[\n\f\r]/.test(token[1].slice(1)) + ) +} + module.exports = class Parser { constructor(input) { this.input = input @@ -335,6 +345,9 @@ module.exports = class Parser { type = token[0] if (type === 'comment' || (type === 'space' && i === length - 1)) { clean = false + } else if (isLineContinuation(token)) { + clean = false + value += token[1].slice(1) } else { value += token[1] } @@ -344,6 +357,8 @@ module.exports = class Parser { let raw = tokens.reduce((all, i) => { if (i[0] === 'comment' && i[4] === 'inline') { return all + '/* ' + i[1].slice(2).trim() + ' */' + } else if (isLineContinuation(i)) { + return all + i[1].slice(1) } else { return all + i[1] } diff --git a/test/cases/backslash.css b/test/cases/backslash.css new file mode 100644 index 0000000..4afa203 --- /dev/null +++ b/test/cases/backslash.css @@ -0,0 +1,14 @@ +@media screen and + (min-width: 600px) { + .one + .two { + margin: 1px + 2px + } +} + +.input { + @apply + px-10 + border-gray-300 +} diff --git a/test/cases/backslash.json b/test/cases/backslash.json new file mode 100644 index 0000000..bdd4acd --- /dev/null +++ b/test/cases/backslash.json @@ -0,0 +1,148 @@ +{ + "raws": { + "indent": " ", + "after": "\n" + }, + "type": "root", + "nodes": [ + { + "raws": { + "before": "", + "afterName": " ", + "params": { + "raw": "screen and\n (min-width: 600px)", + "value": "screen and\n (min-width: 600px)", + "sss": "screen and\\\n (min-width: 600px)" + } + }, + "type": "atrule", + "name": "media", + "source": { + "end": { + "column": 26, + "line": 2, + "offset": 44 + }, + "start": { + "column": 1, + "line": 1, + "offset": 0 + } + }, + "params": "screen and\n (min-width: 600px)", + "nodes": [ + { + "raws": { + "before": "\n ", + "selector": { + "raw": ".one\n .two", + "value": ".one\n .two", + "sss": ".one\\\n .two" + } + }, + "type": "rule", + "nodes": [ + { + "raws": { + "before": "\n ", + "between": ": ", + "value": { + "raw": "1px\n 2px", + "value": "1px\n 2px", + "sss": "1px\\\n 2px" + } + }, + "type": "decl", + "source": { + "end": { + "column": 16, + "line": 6, + "offset": 92 + }, + "start": { + "column": 5, + "line": 5, + "offset": 64 + } + }, + "prop": "margin", + "value": "1px\n 2px" + } + ], + "source": { + "end": { + "column": 7, + "line": 4, + "offset": 59 + }, + "start": { + "column": 3, + "line": 3, + "offset": 47 + } + }, + "selector": ".one\n .two" + } + ] + }, + { + "raws": { + "before": "\n\n" + }, + "type": "rule", + "nodes": [ + { + "raws": { + "before": "\n ", + "afterName": "", + "params": { + "raw": "\n px-10\n border-gray-300", + "value": "\n px-10\n border-gray-300", + "sss": "\\\n px-10\\\n border-gray-300" + } + }, + "type": "atrule", + "name": "apply", + "source": { + "end": { + "column": 20, + "line": 11, + "offset": 141 + }, + "start": { + "column": 3, + "line": 9, + "offset": 103 + } + }, + "params": "\n px-10\n border-gray-300" + } + ], + "source": { + "end": { + "column": 7, + "line": 8, + "offset": 100 + }, + "start": { + "column": 1, + "line": 8, + "offset": 94 + } + }, + "selector": ".input" + } + ], + "source": { + "end": { + "column": 1, + "line": 12, + "offset": 142 + }, + "start": { + "column": 1, + "line": 1, + "offset": 0 + } + } +} diff --git a/test/cases/backslash.sss b/test/cases/backslash.sss new file mode 100644 index 0000000..52479de --- /dev/null +++ b/test/cases/backslash.sss @@ -0,0 +1,11 @@ +@media screen and\ + (min-width: 600px) + .one\ + .two + margin: 1px\ + 2px + +.input + @apply\ + px-10\ + border-gray-300