Automattic / juice

Juice inlines CSS stylesheets into your HTML source.
MIT License
3.1k stars 220 forks source link

Allow SASS syntax #442

Closed filoscoder closed 6 months ago

filoscoder commented 1 year ago

Description

juice has been very useful in improving developer experience to maintain HTML styles. But as it's mentioned in linked issues, it has not the ability to handle syntax like SASS/LESS/Styled-components...

This PR just includes a SASS compiler letting all SASS syntax available.

Below code is fully functional handling & ampersands parent references.

const juice = require('juice');
const result = juice(
  `<html>
    <style>
        .parent {
            color: red;

            & .child {
                color: white;
            }
        }
    </style>
    <div class="parent">
        <p class="child">
            text
        </p>
    </div>
    </html>
    `,
);

console.log('[JUICE RESULT] : ', result);
[JUICE RESULT] :  
  <html>
    <div class="parent" style="color: red;">
        <p class="child" style="color: white;">
            text
        </p>
    </div>
   </html>

Let me know if something is needed

Linked Issues

resolves #403 #392 #390 #378

439 (Not tested)

titanism commented 1 year ago

@filoscoder Just tested this out locally, it seems to be breaking with varying input and causing uncaught error:

sass.Exception [Error]: expected "{".
   ╷
51 │ false
   │      ^
   ╵
  - 51:6  root stylesheet
    at Object.wrapException (/Users/user/Projects/web/node_modules/.pnpm/sass@1.58.0/node_modules/sass/sass.dart.js:1250:17)
    at SpanScanner.error$3$length$position (/Users/user/Projects/web/node_modules/.pnpm/sass@1.58.0/node_modules/sass/sass.dart.js:68140:15)
    at SpanScanner.expectChar$2$name (/Users/user/Projects/web/node_modules/.pnpm/sass@1.58.0/node_modules/sass/sass.dart.js:68222:12)
    at SpanScanner.expectChar$1 (/Users/user/Projects/web/node_modules/.pnpm/sass@1.58.0/node_modules/sass/sass.dart.js:68225:19)
    at ScssParser0.children$1 (/Users/user/Projects/web/node_modules/.pnpm/sass@1.58.0/node_modules/sass/sass.dart.js:91610:10)
    at ScssParser0._stylesheet0$_withChildren$1$3 (/Users/user/Projects/web/node_modules/.pnpm/sass@1.58.0/node_modules/sass/sass.dart.js:97901:39)
    at ScssParser0._stylesheet0$_withChildren$3 (/Users/user/Projects/web/node_modules/.pnpm/sass@1.58.0/node_modules/sass/sass.dart.js:97906:19)
    at ScssParser0._stylesheet0$_styleRule$2 (/Users/user/Projects/web/node_modules/.pnpm/sass@1.58.0/node_modules/sass/sass.dart.js:94968:20)
    at ScssParser0._stylesheet0$_variableDeclarationOrStyleRule$0 (/Users/user/Projects/web/node_modules/.pnpm/sass@1.58.0/node_modules/sass/sass.dart.js:94819:22)
    at ScssParser0._stylesheet0$_statement$1$root (/Users/user/Projects/web/node_modules/.pnpm/sass@1.58.0/node_modules/sass/sass.dart.js:94742:215)
    at StylesheetParser_parse__closure1.call$0 (/Users/user/Projects/web/node_modules/.pnpm/sass@1.58.0/node_modules/sass/sass.dart.js:97968:17)
    at ScssParser0.statements$1 (/Users/user/Projects/web/node_modules/.pnpm/sass@1.58.0/node_modules/sass/sass.dart.js:91676:31)
    at StylesheetParser_parse_closure0.call$0 (/Users/user/Projects/web/node_modules/.pnpm/sass@1.58.0/node_modules/sass/sass.dart.js:97951:23)
    at ScssParser0.wrapSpanFormatException$1$1 (/Users/user/Projects/web/node_modules/.pnpm/sass@1.58.0/node_modules/sass/sass.dart.js:90726:23)
    at ScssParser0.wrapSpanFormatException$1 (/Users/user/Projects/web/node_modules/.pnpm/sass@1.58.0/node_modules/sass/sass.dart.js:90745:19)
    at ScssParser0.parse$0 (/Users/user/Projects/web/node_modules/.pnpm/sass@1.58.0/node_modules/sass/sass.dart.js:94706:19)

I modified it slightly so I could see what's going on:

exports.parseCSS = function(css) {
  var ret = [];
  try {
    var compiledCss = sass.compileString(css).css;
    var parsed = mensch.parse(compiledCss, { position: true, comments: true });
    var rules = typeof parsed.stylesheet != 'undefined' && parsed.stylesheet.rules ? parsed.stylesheet.rules : [];

    for (var i = 0, l = rules.length; i < l; i++) {
      if (rules[i].type == 'rule') {
        var rule = rules[i];
        console.log('rule', rule);
        var selectors = rule.selectors;
        console.log('selectors', selectors);

        for (var ii = 0, ll = selectors.length; ii < ll; ii++) {
          ret.push([selectors[ii], rule.declarations]);
        }
      }
    }
  } catch (err) {
    console.log(err);
  }
  return ret;
};
titanism commented 1 year ago

The above commented error appears to be related to inconsistencies with sass-dart and node-sass.

filoscoder commented 1 year ago

The above commented error appears to be related to inconsistencies with sass-dart and node-sass.

Interesting. Any suggestion?

titanism commented 1 year ago

@filoscoder probably should use https://github.com/posthtml/posthtml-inline-css instead

titanism commented 1 year ago

We could just copy this @filoscoder https://github.com/posthtml/posthtml-inline-css/blob/master/lib/inlineCss.es6 and rewrite with CJS syntax. It's MIT licensed so good to go.

titanism commented 1 year ago

Better option would be to directly use that package - I'm looking into how this is accomplished. We introduced some dark mode css color schemes and the media queries are breaking how this gets parsed and inlined - so that's why we even found this issue in the first place.

titanism commented 1 year ago

We should integrate posthtml and postcss, and then use the exported functions as done here https://github.com/posthtml/posthtml-inline-css/blob/master/lib/inlineCss.es6 from the posthtml-inline-css package.

> require('posthtml-inline-css/lib/helpers')
{
  extendStyle: [Function: extendStyle],
  sortCssNodesBySpecificity: [Function: sortCssNodesBySpecificity],
  getCssFromStyleTags: [Function: getCssFromStyleTags]
}
titanism commented 1 year ago

Be aware of https://github.com/posthtml/posthtml-match-helper/issues/8 and https://github.com/posthtml/posthtml-inline-css/issues/15.

titanism commented 1 year ago

Ignore the above - we don't need to inline CSS anymore, since a majority of clients now support it, so we're dropping inline CSS support in our work. Thank you though.

maheshbvcoder commented 1 year ago

@titanism for which version media query was working correctly