Skip to content

Commit

Permalink
Merge pull request #526 from dminkovsky/fix-482
Browse files Browse the repository at this point in the history
Fix #482: "Error when encoded quotes are used in html attribute"
  • Loading branch information
jrit authored Mar 2, 2025
2 parents ff05e36 + 5d42299 commit 6f0b957
Show file tree
Hide file tree
Showing 8 changed files with 35 additions and 2 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ All juice methods take an options object that can contain any of these propertie

* `applyWidthAttributes` - whether to use any CSS pixel widths to create `width` attributes on elements set in `juice.widthElements`. Defaults to `true`.

* `decodeStyleAttributes` - whether to decode the value of `style=` attributes. Defaults to `false`.

* `extraCss` - extra css to apply to the file. Defaults to `""`.

* `insertPreservedExtraCss` - whether to insert into the document any preserved `@media` or `@font-face` content from `extraCss` when using `preserveMediaQueries`, `preserveFontFaces` or `preserveKeyFrames`. When `true` order of preference to append the `<style>` element is into `head`, then `body`, then at the end of the document. When a `string` the value is treated as a CSS/jQuery/cheerio selector, and when found, the `<style>` tag will be appended to the end of the first match. Defaults to `true`.
Expand Down
1 change: 1 addition & 0 deletions juice.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ declare namespace juice {
xmlMode?: boolean;
preserveImportant?: boolean;
resolveCSSVariables?: boolean;
decodeStyleAttributes?: boolean;
}

interface WebResourcesOptions {
Expand Down
4 changes: 4 additions & 0 deletions lib/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,10 @@ cli.options = {
pMap: 'webResourcesStrict',
map: 'strict',
def: 'see docs for web-resource-inliner strict',
coercion: JSON.parse },
'decode-style-attributes': {
pMap: 'decodeStyleAttributes',
def: 'decode the value of `style=` attributes?',
coercion: JSON.parse }
};

Expand Down
8 changes: 7 additions & 1 deletion lib/inline.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
'use strict';

var { decode } = require('entities');

var utils = require('./utils');
var numbers = require('./numbers');
var variables = require('./variables');
Expand Down Expand Up @@ -149,7 +151,11 @@ function inlineDocument($, css, options) {

// if the element has inline styles, fake selector with topmost specificity
if ($(el).attr(styleAttributeName)) {
var cssText = '* { ' + $(el).attr(styleAttributeName) + ' } ';
var styleAttributeValue = $(el).attr(styleAttributeName);
var cssStyleAttributeValue = options.decodeStyleAttributes
? decode(styleAttributeValue)
: styleAttributeValue;
var cssText = '* { ' + cssStyleAttributeValue + ' } ';
addProps(utils.parseCSS(cssText)[0][1], new utils.Selector('<style>', true));
}

Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"dependencies": {
"cheerio": "^1.0.0",
"commander": "^12.1.0",
"entities": "^4.5.0",
"mensch": "^0.3.4",
"slick": "^1.12.2",
"web-resource-inliner": "^7.0.0"
Expand Down
1 change: 1 addition & 0 deletions test/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ it('cli parses options', function(done) {
assert.strictEqual(parseArgs({'applyAttributesTableElements': 'true'}).applyAttributesTableElements, true);
assert.strictEqual(parseArgs({'xmlMode': 'true'}).xmlMode, true);
assert.strictEqual(parseArgs({'resolveCSSVariables': 'true'}).resolveCSSVariables, true);
assert.strictEqual(parseArgs({'decodeStyleAttributes': 'true'}).decodeStyleAttributes, true);
assert.strictEqual(parseArgs({'webResourcesInlineAttribute': 'true'}).webResources.inlineAttribute, true);
assert.strictEqual(parseArgs({'webResourcesImages': '12'}).webResources.images, 12);
assert.strictEqual(parseArgs({'webResourcesLinks': 'true'}).webResources.links, true);
Expand Down
18 changes: 18 additions & 0 deletions test/juice.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -192,3 +192,21 @@ it('test that preserved text order is stable', function() {
utils.getPreservedText('div { color: red; } @media (min-width: 320px) { div { color: green; } } @media (max-width: 640px) { div { color: blue; } }', { mediaQueries: true }, juice.ignoredPseudos).replace(/\s+/g, ' '),
' @media (min-width: 320px) { div { color: green; } } @media (max-width: 640px) { div { color: blue; } } ');
});

it('can handle style attributes with html entities', function () {
// Throws without decodeStyleAttributes: true
assert.throws(function () {
juice(
'<style type="text/css">div {color: red;}</style><div style="font-family:&quot;Open Sans&quot;, sans-serif;"></div>'
);
});

// Expected results with decodeStyleAttributes: true
assert.deepEqual(
juice(
'<style type="text/css">div {color: red;}</style><div style="font-family:&quot;Open Sans&quot;, sans-serif;"></div>',
{ decodeStyleAttributes: true }
),
"<div style=\"color: red; font-family: 'Open Sans', sans-serif;\"></div>"
);
});

0 comments on commit 6f0b957

Please sign in to comment.