Giter Site home page Giter Site logo

juice's Introduction

Build Status Dependency Status

Juice

Given HTML, juice will inline your CSS properties into the style attribute.

Some projects using Juice

How to use

Juice has a number of functions based on whether you want to process a file, HTML string, or a cheerio document, and whether you want juice to automatically get remote stylesheets, scripts and image dataURIs to inline.

To inline HTML without getting remote resources, using default options:

var juice = require('juice');
var result = juice("<style>div{color:red;}</style><div/>");

result will be:

<div style="color: red;"></div>

Try out the web client version

What is this useful for ?

  • HTML emails. For a comprehensive list of supported selectors see here
  • Embedding HTML in 3rd-party websites.

Documentation

Juice is exposed as a standard module, and from CLI with a smaller set of options.

Options

All juice methods take an options object that can contain any of these properties, though not every method uses all of these:

  • applyAttributesTableElements - whether to create attributes for styles in juice.styleToAttribute on elements set in juice.tableElements. Defaults to true.

  • applyHeightAttributes - whether to use any CSS pixel heights to create height attributes on elements set in juice.heightElements. Defaults to true.

  • applyStyleTags - whether to inline styles in <style></style> Defaults to true.

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

  • 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.

  • inlinePseudoElements - Whether to insert pseudo elements (::before and ::after) as <span> into the DOM. Note: Inserting pseudo elements will modify the DOM and may conflict with CSS selectors elsewhere on the page (e.g., :last-child).

  • preserveFontFaces - preserves all @font-face within <style></style> tags as a refinement when removeStyleTags is true. Other styles are removed. Defaults to true.

  • preserveImportant - preserves !important in values. Defaults to false.

  • preserveMediaQueries - preserves all media queries (and contained styles) within <style></style> tags as a refinement when removeStyleTags is true. Other styles are removed. Defaults to true.

  • preserveKeyFrames - preserves all key frames within <style></style> tags as a refinement when removeStyleTags is true. Other styles are removed. Defaults to true.

  • preservePseudos - preserves all rules containing pseudo selectors defined in ignoredPseudos within <style></style> tags as a refinement when removeStyleTags is true. Other styles are removed. Defaults to true.

  • removeStyleTags - whether to remove the original <style></style> tags after (possibly) inlining the css from them. Defaults to true.

  • resolveCSSVariables - whether to resolve CSS variables. Defaults to true.

  • webResources - An options object that will be passed to web-resource-inliner for juice functions that will get remote resources (juiceResources and juiceFile). Defaults to {}.

  • xmlMode - whether to output XML/XHTML with all tags closed. Note that the input must also be valid XML/XHTML or you will get undesirable results. Defaults to false.

Methods

juice(html [, options])

Returns string containing inlined HTML. Does not fetch remote resources.

  • html - html string, accepts complete documents as well as fragments
  • options - optional, see Options above

juice.juiceResources(html, options, callback)

Callback returns string containing inlined HTML. Fetches remote resources.

  • html - html string
  • options - see Options above
  • callback(err, html)
    • err - Error object or null
    • html - inlined HTML

juice.juiceFile(filePath, options, callback)

Callback returns string containing inlined HTML. Fetches remote resources.

  • filePath - path to the html file to be juiced
  • options - see Options above
  • callback(err, html)
    • err - Error object or null
    • html - inlined HTML

juice.juiceDocument($ [, options])

This takes a cheerio instance and performs inlining in-place. Returns the same cheerio instance. Does not fetch remote resources.

  • $ - a cheerio instance, be sure to use the same cheerio version that juice uses
  • options - optional, see Options above`

juice.inlineContent(html, css [, options])

This takes html and css and returns new html with the provided css inlined. It does not look at <style> or <link rel="stylesheet"> elements at all.

  • html - html string
  • css - css string
  • options - optional, see Options above

juice.inlineDocument($, css [, options])

Given a cheerio instance and css, this modifies the cheerio instance so that the provided css is inlined. It does not look at <style> or <link rel="stylesheet"> elements at all.

  • $ - a cheerio instance, be sure to use the same cheerio version that juice uses
  • css - css string
  • options - optional, see Options above

Global settings

juice.codeBlocks

An object where each value has a start and end to specify fenced code blocks that should be ignored during parsing and inlining. For example, Handlebars (hbs) templates are juice.codeBlocks.HBS = {start: '{{', end: '}}'}. codeBlocks can fix problems where otherwise juice might interpret code like <= as HTML, when it is meant to be template language code. Note that codeBlocks is a dictionary which can contain many different code blocks, so don't do juice.codeBlocks = {...} do juice.codeBlocks.myBlock = {...}

juice.ignoredPseudos

Array of ignored pseudo-selectors such as 'hover' and 'active'.

juice.widthElements

Array of HTML elements that can receive width attributes.

juice.heightElements

Array of HTML elements that can receive height attributes.

juice.styleToAttribute

Object of style property names (key) to their respective attribute names (value).

juice.tableElements

Array of table HTML elements that can receive attributes defined in juice.styleToAttribute.

juice.nonVisualElements

Array of elements that will not have styles inlined because they are not intended to render.

juiceClient.excludedProperties

Array of css properties that won't be inlined.

Special markup

data-embed

When a data-embed attribute is present on a stylesheet <link> that has been inlined into the document as a <style></style> tag by the web-resource-inliner juice will not inline the styles and will not remove the <style></style> tags.

This can be used to embed email client support hacks that rely on css selectors into your email templates.

CLI Options

To use Juice from CLI, run juice [options] input.html output.html

For a listing of all available options, just type juice -h.

Note that if you want to just type juice from the command line, you should npm install juice -g so it is globally available.

CLI Options:

The CLI should have all the above options with the names changed from camel case to hyphen-delimited, so for example extraCss becomes extra-css and webResources.scripts becomes web-resources-scripts.

These are additional options not included in the standard juice options listed above:

  • --css [filepath] will load and inject CSS into extraCss.
  • --options-file [filepath] will load and inject options from a JSON file. Options from the CLI will be given priority over options in the file when there is a conflict.
  • codeBlocks is optionally supported in the options file if you include it. This will allow you to support different template languages in a build process.

Running Juice in the Browser

Attempting to Browserify require('juice') fails because portions of Juice and its dependencies interact with the file system using the standard require('fs'). However, you can require('juice/client') via Browserify which has support for juiceDocument, inlineDocument, and inlineContent, but not juiceFile, juiceResources, or inlineExternal. Note that automated tests are not running in the browser yet.

License

MIT Licensed, see License.md

3rd-party

juice's People

Contributors

andrewrk avatar arlolra avatar bago avatar binarykitchen avatar calebmer avatar cbumgard avatar codeclown avatar cossssmin avatar dantman avatar dependabot[bot] avatar dilumn avatar gerhobbelt avatar hansottowirtz avatar hectormenendez avatar jgannonjr avatar jrit avatar leemunroe avatar m1san avatar manobi avatar maximilianredt avatar mieko avatar mnorton-unity avatar nb avatar niftylettuce avatar nrschultz avatar parshap avatar rauchg avatar tootallnate avatar trysound avatar zemirco avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

juice's Issues

npm install fails on Windows8.1 x64

We're getting the following error:

C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\Microsoft.Cpp.InvalidPlatform
.Targets(23,7): error MSB8007: The Platform for project 'contextify.vcxproj' is
invalid. Platform='x64'. You may be seeing this message because you are tryin
g to build a project without a solution file, and have specified a non-default
Platform that doesn't exist for this project. [C:\Users\yossi\node_modules\juic
e\node_modules\jsdom\node_modules\contextify\build\contextify.vcxproj]

event loop blocked: update the slick module

I'm not sure what is going on, but it seems some of our users used a very broken html, then juice called slick with this:

var s = new Selector('</p>\n<h1 id="outlook-a-">outlook a')
s.parsed(); //blocks the event loop

When parsed is call, the eventloop is blocked, maybe an infinite loop or something like that.

After updating to [email protected] the problem is gone.

Consider using <font> tags for Outlook compatibility?

I'm just starting to dig in to the mess of mail client compatibility, so I could be wrong here, but it seems like some versions of Outlook (2007, 2010, and 2013) will ignore font styles in inline style="" attributes. The only way I can find to make these clients render the desired font is to use a <font> tag, like it was 1999.

This is obviously painful. But it would be a lot more painful to have to code this by hand than to find a tool which would do it. I'm willing to work on a pull request for this feature if you think it belongs in juice.

As I envision it, there would be an option available, like inlineFontTags, which would default to false. If it were set to true, then every element which had a font-family, font-size, or color applied to it would also have its content wrapped in a a <font> tag with the appropriate face, color, and size attributes.

Does this make sense, and is it something you see as worth including in juice?

Using Juice on the client-side

This is the error I get when I try to use juice.inlineContent(html, css):

/Users/holmes/Code/foobar/app/node_modules/browserify/lib/wrap.js:445
            throw moduleError('Cannot find module');
                  ^
Error: Cannot find module: "canvas" from directory "/Users/holmes/Code/foobar/app/node_modules/juice/node_modules/jsdom/lib/jsdom/level2" while processing file /Users/holmes/Code/foobar/app/node_modules/juice/node_modules/jsdom/lib/jsdom/level2/html.js
    at moduleError (/Users/holmes/Code/foobar/app/node_modules/browserify/lib/wrap.js:406:16)
    at Wrap.require (/Users/holmes/Code/foobar/app/node_modules/browserify/lib/wrap.js:445:19)
    at Wrap.require (/Users/holmes/Code/foobar/app/node_modules/browserify/lib/wrap.js:537:14)
    at Array.forEach (native)
    at Wrap.require (/Users/holmes/Code/foobar/app/node_modules/browserify/lib/wrap.js:519:27)
    at Wrap.require (/Users/holmes/Code/foobar/app/node_modules/browserify/lib/wrap.js:537:14)
    at Array.forEach (native)
    at Wrap.require (/Users/holmes/Code/foobar/app/node_modules/browserify/lib/wrap.js:519:27)
    at Wrap.require (/Users/holmes/Code/foobar/app/node_modules/browserify/lib/wrap.js:537:14)
    at Array.forEach (native)

I'm using Juice client-side via browserify 1.15.

Can Juice work on the client-side? I don't see why it couldn't.

Update node modules

You have some outdated modules:

cssom node_modules/juice/node_modules/cssom current=0.2.5 wanted=0.2.5 latest=0.3.0
batch node_modules/juice/node_modules/batch current=0.3.2 wanted=0.3.2 latest=0.5.0
jsdom node_modules/juice/node_modules/jsdom current=0.6.5 wanted=0.6.5 latest=0.8.9
superagent node_modules/juice/node_modules/superagent current=0.14.9 wanted=0.14.9 latest=0.15.7
commander node_modules/juice/node_modules/commander current=1.1.1 wanted=1.1.1 latest=2.1.0

Thanks for updating soon ...

juice should not change my <br> to <br />

My code uses <br> html syntax, which for some reason gets changed to <br />.

Can we remove this behavior? Given that it is a CSS inliner, it really shouldn't touch other parts of my DOM.

Is juice still alive ?

Just asking, I don't see many alternatives out there, it would be nice to know what happened to this project. tx.

preserve vendor prefixes

Is it possible to preserve vendor prefixes? It seems all of my gradients are stripped out, down to just linear-gradient() and filter:..

I'm using the gradient generator here: http://www.colorzilla.com/gradient-editor/
I need to preserve -webkit, etc.

This:

background: #ffffff; background: -moz-linear-gradient(top,  #ffffff 0%, #e5e5e5 100%); background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#ffffff), color-stop(100%,#e5e5e5)); background: -webkit-linear-gradient(top,  #ffffff 0%,#e5e5e5 100%); background: -o-linear-gradient(top,  #ffffff 0%,#e5e5e5 100%); background: -ms-linear-gradient(top,  #ffffff 0%,#e5e5e5 100%); background: linear-gradient(to bottom,  #ffffff 0%,#e5e5e5 100%); filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#e5e5e5',GradientType=0 ); ```

is stripped down to this:

background: linear-gradient(to bottom,  #ffffff 0%,#e5e5e5 100%); filter: progid:DXImageTransform.Microsoft.gradient( startColorstr=#ffffff, endColorstr=#e5e5e5,GradientType=0 );

Disable rewriting of language or template specific parts.

At the moment the following isn't working, when you make use of TemplateParsers:

// from
<p>Dear {{ $person->user->firstname }},</p>
// to
<p>Dear {{ $person-&gt;user-&gt;firstname }},</p>

And the following code is also incorrect parsed:

// from
<?php
$demo = Demo::whereEmail($email)->first();
?>
// to
first();
?&gt;

Problem with 'filter' property

juice fails with Error: Unmatched ' for this style:
filter: progidDXImageTransform.Microsoft.gradient(startColorstr='argb(rgba(0,0,0,0.5))', endColorstr='argb(rgba(0,0,0,0))', GradientType=1)

I can't use bootstrap (actually bootstrap-stylus) with juice because of this bug.

unable to handle media queriers

Great plugin BTW, but when I am trying to inline a mobile friendly email, the defaults just throw away the media queries. I know I have the option of not deleting them, but it would be nice to only delete the ones that got applied.

I guess an alternative would be to internal CSS (head style block) the media query's and external everything else and remove just the external ones.

Thanks!

bug: not ignoring pseudos

Input:

a:link, a:visited {
  font-weight: bold;
}
<html><body>
<a href="#">Test</a>
</body></html>

Expected:

<html><body>
<a href="#">Test</a>
</body></html>

Actual

<html><body>
<a href="#" style="font-weight: bold;">Test</a>
</body></html>

Crash when requiring

It's crashing since the last csstyle update (I suppose)

Error: Cannot find module './properties'
at Function.Module._resolveFilename (module.js:338:15)
at Function.Module._load (module.js:280:25)
at Module.require (module.js:364:17)
at require (module.js:380:17)
at Object. (C:\Users\Julio\Desktop\visitavirtual-api\node_modules
\juice\node_modules\jsdom\node_modules\cssstyle\lib\CSSStyleDeclaration.js:209:1
)
at Module._compile (module.js:456:26)
at Object.Module._extensions..js (module.js:474:10)
at Module.load (module.js:356:32)
at Function.Module._load (module.js:312:12)
at Module.require (module.js:364:17)

Stripping php tags

For some reason the plugin is producing undesired effects on PHP tags within the email:

<a href="<?php echo user()->getSystemAddress() ?>" target="_blank">

gets compiled to:

<a href="&lt;?php echo user()-&gt;getSystemAddress() ?&gt;" target="_blank" style="color: #348eda;">

While others like:

<h3><?php echo $code ?></h3>

are compiled to:

<h3 style="font-family: Helvetica, Arial, sans-serif;"></h3>

Is there anything I can do to avoid this?

Release to NPM

Some bug fixes are not available in the NPM package.
For example fix #6 which has been fixed 6 months ago is not included in 0.0.6.

Improper CSS is ignored; effect cascades (CLI)

Some poorly formatted CSS properties are ignored by juice. But the ignoring cascades, and affects every other property in the stylesheet. Consequently, anything before the poor CSS is inlined as it should be, while everything after the poor CSS is ignored.

Example:

index.html

<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Testing</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <table>
    <tr>
      <td><a href="sdf">a test!</a></td>
    </tr>
  </table>
</body>
</html>

style.css

body {
  padding: 5px;
}
a {
  color: ;
}
table {
  padding: 5px;
  color: #333;
}

output.html

<html lang="en">
<head>
  <meta charset="UTF-8" />
  <title>Testing</title>

</head>
<body style="padding: 5px;">
  <table>
    <tr>
      <td><a href="sdf" style="color: #333;">a test!</a></td>
    </tr>
  </table>
</body>
</html>

The expected result would give padding: 5px; and color: #333 to the table. And it will do this if you remove color: ; from the stylesheet.

I would expect color: ; to be ignored and the rest of the stylesheet to be processed as usual.

Fails with @media queries

@media only screen and (-webkit-min-device-pixel-ratio: 2) {
  /* Put your iPhone 4g styles in here */ 
}

When using media queries for mobile targeting, juice fails with this error:

/nodeproject/node_modules/email-templates/node_modules/juice/lib/utils.js:23
  for (var i = 0, l = selectorText.length; i < l; i++) {
                                  ^
TypeError: Cannot read property 'length' of undefined
    at Object.extract (/nodeproject/node_modules/email-templates/node_modules/juice/lib/utils.js:23:35)
    at Object.exports.parseCSS (/nodeproject/node_modules/email-templates/node_modules/juice/lib/utils.js:60:29)
    at juice (/nodeproject/node_modules/email-templates/node_modules/juice/lib/juice.js:61:21)
    at EmailTemplate.render (/nodeproject/node_modules/email-templates/lib/main.js:40:28)
    at fs.stat.batchCheck (/nodeproject/node_modules/email-templates/lib/main.js:79:19)
    at fs.stat.fs.stat.fs.stat.fs.readFile.async.map.fs.readFile.that.text (/nodeproject/node_modules/email-templates/lib/main.js:132:19)
    at _asyncMap (/nodeproject/node_modules/email-templates/node_modules/async/lib/async.js:190:13)
    at async.forEach (/nodeproject/node_modules/email-templates/node_modules/async/lib/async.js:94:25)
    at _asyncMap (/nodeproject/node_modules/email-templates/node_modules/async/lib/async.js:187:17)
    at fs.readFile (fs.js:176:14)

CSS wildcard affects subsequent single-property selectors

CSS wildcards such as * { margin:0; padding:0; } are very common in HTML email CSS, often as the first ruleset. I noticed a strange behavior today. For this test I used an HTML page with a single tag

<h1>The Leading Source for Private Mortgages</h1>

And this CSS in a linked stylesheet:

* { padding: 0; }
h1 { padding-top: 10px; }
h1 { padding: 0; }

I expected the finally h1 to result in no padding but instead the inline style was:

style="padding: 0; padding-top: 10px;"

If I remove the wildcard selector:

h1 { padding-top: 10px; }
h1 { padding: 0; }

I get the expected result:

style="padding-top: 10px; border: 0;"

So the wildcard seems to be the problem. I also noticed that if I return to the original CSS and use the shorthand padding instead of padding-top in the h1 selector:

* { padding: 0; }
h1 { padding: 10px 0 0 0; }
h1 { padding: 0; }

It now cascades correctly and is the cleanest:

style="padding: 0;"

The solution for now seems to be to use shorthand properties, i.e., padding instead of padding-top. One other test I did was to see what happened if I listed each property as a separate declaration:

* { padding: 0; }
h1 { padding-top: 10px; padding-left: 0; padding-right: 0; padding-bottom: 0; }
h1 { padding: 0; }

This also failed to cascade properly:

style="padding: 0; padding-top: 10px; padding-left: 0; padding-right: 0; padding-bottom: 0;"

Conditional Tags Handling

Hello,

how could the conditional tags could be treated?
I've stumbled upon the email boilerplate and thus, I've wondered if some CSS could remain between <style> tags in certain circumstances.

Basically, those styles (if external and between conditional tags) should be inlined between the conditional tags.

Does this sound good?

Juice runs, but doesn't inline anything

I'm using juice to inline some CSS in a fairly simple email template:

var juice = require('juice');
juice(srcPath, opts, function(err, html) {
  grunt.file.write(destFile, html);
});

This is not complete code but this is the part that juice touches. The srcPath/file and destFile are valid. Juice seems to run successfully as far as I can tell, and the destFile does contain html, just not the html I would expect.

Here is the src html:

<!DOCTYPE html public "-//w3c//dtd xhtml 1.0 strict//en" "http://www.w3.org/tr/xhtml1/dtd/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1.0"/><title>Email Tempalte</title><style type="text/css">/* Based on The MailChimp Reset INLINE: Yes. */
/* Client-specific Styles */
#outlook a {padding:0;} /* Force Outlook to provide a "view in browser" menu link. */
body{width:100% !important; -webkit-text-size-adjust:100%; -ms-text-size-adjust:100%; margin:0; padding:0;}
/* Prevent Webkit and Windows Mobile platforms from changing default font sizes.*/
.ExternalClass {width:100%;} /* Force Hotmail to display emails at full width */
.ExternalClass, .ExternalClass p, .ExternalClass span, .ExternalClass font, .ExternalClass td, .ExternalClass div {line-height: 100%;}
/* Forces Hotmail to display normal line spacing.  More on that: http://www.emailonacid.com/forum/viewthread/43/ */
#background-table {margin:0; padding:0; width:100% !important; line-height: 100% !important;}
/* End reset */
</style><link rel="stylesheet" type="text/css" href="styles/style.css"/><!--[if IEMobile 7]><style type="text/css"></style><![endif]--><!--[if gte mso 9]><style></style><![endif]--></head><body><table id="background-table" cellpadding="0" cellspacing="0" border="0"><tr><td height="75" class="responsive-header-footer-space"></td></tr><tr><td valign="top"><table id="content-table" cellpadding="0" cellspacing="0" border="0" align="center"><tr><td width="40" height="35" valign="top"></td><td width="470" height="35" valign="top"></td><td width="40" height="35" valign="top"></td></tr><tr><td width="40" valign="top"></td><td width="470" valign="top"></td><td width="40" valign="top"></td></tr><tr><td width="40" height="35" valign="top"></td><td width="470" height="35" valign="top"></td><td width="40" height="35" valign="top"></td></tr></table></td></tr><tr><td height="75" class="responsive-header-footer-space"></td></tr></table></body></html>

And here is the html output by Juice:

<html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Email Tempalte</title><!--[if IEMobile 7]><style type="text/css"></style><![endif]--><!--[if gte mso 9]><style></style><![endif]--></head><body><table id="background-table" cellpadding="0" cellspacing="0" border="0"><tr><td height="75" class="responsive-header-footer-space"></td></tr><tr><td valign="top"><table id="content-table" cellpadding="0" cellspacing="0" border="0" align="center"><tr><td width="40" height="35" valign="top"></td><td width="470" height="35" valign="top"></td><td width="40" height="35" valign="top"></td></tr><tr><td width="40" valign="top"></td><td width="470" valign="top"></td><td width="40" valign="top"></td></tr><tr><td width="40" height="35" valign="top"></td><td width="470" height="35" valign="top"></td><td width="40" height="35" valign="top"></td></tr></table></td></tr><tr><td height="75" class="responsive-header-footer-space"></td></tr></table></body></html>

Most noteworthy:

  • DOCTYPE is completely ommitted.
  • No CSS is inlined at all: neither from the <style> block or from the linked stylesheets.

The source HTML validates, so I have no idea what could be going wrong. Any help would be greatly appreciated.

inlineContent error

juice = require 'juice'
juice.inlineContent email_html, (err, html) ->
  console.log arguments

Produces:

/Users/cory/Projects/nodejs/Titan/aladdinsupplyco/node_modules/juice/node_modules/cssom/lib/parse.js:60
    for (var character; character = token.charAt(i); i++) {
                                          ^
TypeError: Object function (err, html) {
        console.log(arguments);
        return send({
          success: true
        });
      } has no method 'charAt'
    at Object.parse (/Users/cory/Projects/nodejs/Titan/aladdinsupplyco/node_modules/juice/node_modules/cssom/lib/parse.js:60:40)
    at Object.exports.parseCSS (/Users/cory/Projects/nodejs/Titan/aladdinsupplyco/node_modules/juice/lib/utils.js:56:21)
    at inlineDocument (/Users/cory/Projects/nodejs/Titan/aladdinsupplyco/node_modules/juice/lib/juice.js:61:21)
    at Function.inlineContent (/Users/cory/Projects/nodejs/Titan/aladdinsupplyco/node_modules/juice/lib/juice.js:192:3)
    at res.render.first_name (eval at <anonymous> (/Users/cory/Projects/nodejs/Titan/aladdinsupplyco/node_modules/compound/node_modules/kontroller/lib/base.js:157:17))
    at fs.readFile (fs.js:176:14)
    at Object.oncomplete (fs.js:297:15)

jsdom too slow

Hey guys,

We had to move away from juice, because jsdom is taking more than 5 minutes to parse long html (1Mb).

I found a solution using cheerio which is way faster.

Tell me if you need more information about it.

Best,
Philmod

do less

I think stuff like superagent are better left to modules written on top of juice. Really I would have juice be as "dumb" as possible and leave all the magical things to other modules.

The benefits are less code, easier to maintain, and easier to test.

Update node modules

If you run npm outdated you will see you have quite a lot of outdated modules. Would be good to update them soon. Thx.

Unbale to install through the Node.js Tools for Visual Studio packae manager

I am having trouble installing the juice package using the Node.js Tools for Visual Studio package manager. It fails to install with the following console output:

====Executing command 'npm install juice --save-dev '====

npm http GET https://registry.npmjs.org/juice
npm http 304 https://registry.npmjs.org/juice
npm http GET https://registry.npmjs.org/cssom
npm http GET https://registry.npmjs.org/batch
npm http GET https://registry.npmjs.org/superagent
npm http GET https://registry.npmjs.org/jsdom
npm http GET https://registry.npmjs.org/commander
npm http GET https://registry.npmjs.org/slick
npm http 304 https://registry.npmjs.org/superagent
npm http 304 https://registry.npmjs.org/cssom
npm http 304 https://registry.npmjs.org/commander
npm http 304 https://registry.npmjs.org/jsdom
npm http 304 https://registry.npmjs.org/batch
npm http 304 https://registry.npmjs.org/slick
npm http GET https://registry.npmjs.org/keypress
npm http GET https://registry.npmjs.org/qs/0.6.5
npm http GET https://registry.npmjs.org/formidable/1.0.9
npm http GET https://registry.npmjs.org/emitter-component/1.0.0
npm http GET https://registry.npmjs.org/methods/0.0.1
npm http GET https://registry.npmjs.org/mime/1.2.5
npm http GET https://registry.npmjs.org/cookiejar/1.3.0
npm http GET https://registry.npmjs.org/debug
npm http GET https://registry.npmjs.org/nwmatcher
npm http GET https://registry.npmjs.org/cssstyle
npm http GET https://registry.npmjs.org/htmlparser2
npm http GET https://registry.npmjs.org/contextify
npm http GET https://registry.npmjs.org/request
npm http 304 https://registry.npmjs.org/keypress
npm http 304 https://registry.npmjs.org/formidable/1.0.9
npm http 304 https://registry.npmjs.org/debug
npm http 304 https://registry.npmjs.org/qs/0.6.5
npm http 304 https://registry.npmjs.org/cssstyle
npm http 304 https://registry.npmjs.org/request
npm http 304 https://registry.npmjs.org/methods/0.0.1
npm http 304 https://registry.npmjs.org/nwmatcher
npm http 304 https://registry.npmjs.org/contextify
npm http 304 https://registry.npmjs.org/emitter-component/1.0.0
npm http 304 https://registry.npmjs.org/cookiejar/1.3.0
npm http 304 https://registry.npmjs.org/mime/1.2.5
npm http 304 https://registry.npmjs.org/htmlparser2
npm http GET https://registry.npmjs.org/bindings
npm http GET https://registry.npmjs.org/node-uuid
npm http GET https://registry.npmjs.org/json-stringify-safe
npm http GET https://registry.npmjs.org/qs
npm http GET https://registry.npmjs.org/forever-agent
npm http GET https://registry.npmjs.org/tough-cookie
npm http GET https://registry.npmjs.org/form-data
npm http GET https://registry.npmjs.org/mime
npm http GET https://registry.npmjs.org/http-signature
npm http GET https://registry.npmjs.org/tunnel-agent
npm http GET https://registry.npmjs.org/oauth-sign
npm http GET https://registry.npmjs.org/aws-sign2
npm http GET https://registry.npmjs.org/hawk
npm http GET https://registry.npmjs.org/domhandler
npm http GET https://registry.npmjs.org/domutils
npm http GET https://registry.npmjs.org/domelementtype
npm http GET https://registry.npmjs.org/readable-stream
npm http 304 https://registry.npmjs.org/node-uuid
npm http 304 https://registry.npmjs.org/readable-stream
npm http 304 https://registry.npmjs.org/qs
npm http 304 https://registry.npmjs.org/bindings
npm http 304 https://registry.npmjs.org/http-signature
npm http 304 https://registry.npmjs.org/forever-agent
npm http 304 https://registry.npmjs.org/json-stringify-safe
npm http 304 https://registry.npmjs.org/oauth-sign
npm http 304 https://registry.npmjs.org/mime
npm http 304 https://registry.npmjs.org/tough-cookie
npm http 304 https://registry.npmjs.org/form-data
npm http 304 https://registry.npmjs.org/tunnel-agent
npm http 304 https://registry.npmjs.org/hawk

[email protected] install D:\Projects\ModernMail\ModernMail\node_modules\juice\node_modules\jsdom\node_modules\contextify
node-gyp rebuild

D:\Projects\ModernMail\ModernMail\node_modules\juice\node_modules\jsdom\node_modules\contextify>node "C:\Program Files\nodejs\node_modules\npm\bin\node-gyp-bin....\node_modules\node-gyp\bin\node-gyp.js" rebuild
gypnpm http 304 https://registry.npmjs.org/aws-sign2
npm http 304 https://registry.npmjs.org/domelementtype
npm http 304 https://registry.npmjs.org/domutils
npm http 304 https://registry.npmjs.org/domhandler
npm http GET https://registry.npmjs.org/combined-stream
npm http GET https://registry.npmjs.org/async
npm http GET https://registry.npmjs.org/assert-plus/0.1.2
npm http GET https://registry.npmjs.org/asn1/0.1.11
npm http GET https://registry.npmjs.org/ctype/0.5.2
npm ERR! Error: ENOENT, lstat 'D:\Projects\ModernMail\ModernMail\node_modules\juice\node_modules\jsdom\node_modules\htmlparser2\node_modules\readable-stream\float.patch'
npm ERR! If you need help, you may report this entire log,
npm ERR! including the npm and node versions, at:
npm ERR! http://github.com/isaacs/npm/issues

npm ERR! System Windows_NT 6.1.7601
npm ERR! command "C:\Program Files\nodejs\node.exe" "C:\Program Files\nodejs\node_modules\npm\bin\npm-cli.js" "install" "juice" "--save-dev"
npm ERR! cwd D:\Projects\ModernMail\ModernMail
npm ERR! node -v v0.10.25
npm ERR! npm -v 1.3.24
npm ERR! path D:\Projects\ModernMail\ModernMail\node_modules\juice\node_modules\jsdom\node_modules\htmlparser2\node_modules\readable-stream\float.patch
npm ERR! fstream_path D:\Projects\ModernMail\ModernMail\node_modules\juice\node_modules\jsdom\node_modules\htmlparser2\node_modules\readable-stream\float.patch
npm ERR! fstream_type File
npm ERR! fstream_class FileWriter
npm ERR! code ENOENT
npm ERR! errno 34
npm ERR! fstream_stack C:\Program Files\nodejs\node_modules\npm\node_modules\fstream\lib\writer.js:284:26
npm ERR! fstream_stack Object.oncomplete (fs.js:107:15)
npm ERR! Error: ENOENT, lstat 'D:\Projects\ModernMail\ModernMail\node_modules\juice\node_modules\jsdom\node_modules\htmlparser2\node_modules\domhandler\test\cases\13-comment_in_text.json'
npm ERR! If you need help, you may report this entire log,
npm ERR! including the npm and node versions, at:
npm ERR! http://github.com/isaacs/npm/issues

npm ERR! System Windows_NT 6.1.7601
npm ERR! command "C:\Program Files\nodejs\node.exe" "C:\Program Files\nodejs\node_modules\npm\bin\npm-cli.js" "install" "juice" "--save-dev"
npm ERR! cwd D:\Projects\ModernMail\ModernMail
npm ERR! node -v v0.10.25
npm ERR! npm -v 1.3.24
npm ERR! path D:\Projects\ModernMail\ModernMail\node_modules\juice\node_modules\jsdom\node_modules\htmlparser2\node_modules\domhandler\test\cases\13-comment_in_text.json
npm ERR! fstream_path D:\Projects\ModernMail\ModernMail\node_modules\juice\node_modules\jsdom\node_modules\htmlparser2\node_modules\domhandler\test\cases\13-comment_in_text.json
npm ERR! fstream_type File
npm ERR! fstream_class FileWriter
npm ERR! code ENOENT
npm ERR! errno 34
npm ERR! fstream_stack C:\Program Files\nodejs\node_modules\npm\node_modules\fstream\lib\writer.js:284:26
npm ERR! fstream_stack Object.oncomplete (fs.js:107:15)
npm ERR! Error: ENOENT, lstat 'D:\Projects\ModernMail\ModernMail\node_modules\juice\node_modules\jsdom\node_modules\request\node_modules\hawk\images\logo.png'
npm ERR! If you need help, you may report this entire log,
npm ERR! including the npm and node versions, at:
npm ERR! http://github.com/isaacs/npm/issues

npm ERR! System Windows_NT 6.1.7601
npm ERR! command "C:\Program Files\nodejs\node.exe" "C:\Program Files\nodejs\node_modules\npm\bin\npm-cli.js" "install" "juice" "--save-dev"
npm ERR! cwd D:\Projects\ModernMail\ModernMail
npm ERR! node -v v0.10.25
npm ERR! npm -v 1.3.24
npm ERR! path D:\Projects\ModernMail\ModernMail\node_modules\juice\node_modules\jsdom\node_modules\request\node_modules\hawk\images\logo.png
npm ERR! fstream_path D:\Projects\ModernMail\ModernMail\node_modules\juice\node_modules\jsdom\node_modules\request\node_modules\hawk\images\logo.png
npm ERR! fstream_type File
npm ERR! fstream_class FileWriter
npm ERR! code ENOENT
npm ERR! errno 34
npm ERR! fstream_stack C:\Program Files\nodejs\node_modules\npm\node_modules\fstream\lib\writer.js:284:26
npm ERR! fstream_stack Object.oncomplete (fs.js:107:15)
npm ERR! Error: ENOENT, lstat 'D:\Projects\ModernMail\ModernMail\node_modules\juice\node_modules\jsdom\node_modules\request\node_modules\tough-cookie\lib\cookie.js'
npm ERR! If you need help, you may report this entire log,
npm ERR! including the npm and node versions, at:
npm ERR! http://github.com/isaacs/npm/issues

npm ERR! System Windows_NT 6.1.7601
npm ERR! command "C:\Program Files\nodejs\node.exe" "C:\Program Files\nodejs\node_modules\npm\bin\npm-cli.js" "install" "juice" "--save-dev"
npm ERR! cwd D:\Projects\ModernMail\ModernMail
npm ERR! node -v v0.10.25
npm ERR! npm -v 1.3.24
npm ERR! path D:\Projects\ModernMail\ModernMail\node_modules\juice\node_modules\jsdom\node_modules\request\node_modules\tough-cookie\lib\cookie.js
npm ERR! fstream_path D:\Projects\ModernMail\ModernMail\node_modules\juice\node_modules\jsdom\node_modules\request\node_modules\tough-cookie\lib\cookie.js
npm ERR! fstream_type File
npm ERR! fstream_class FileWriter
npm ERR! code ENOENT
npm ERR! errno 34
npm ERR! fstream_stack C:\Program Files\nodejs\node_modules\npm\node_modules\fstream\lib\writer.js:284:26
npm ERR! fstream_stack Object.oncomplete (fs.js:107:15)
npm ERR! [email protected] install: node-gyp rebuild
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the [email protected] install script.
npm ERR! This is most likely a problem with the contextify package,
npm ERR! not with npm itself.
npm ERR! Tell the author that this fails on your system:
npm ERR! node-gyp rebuild
npm ERR! You can get their info via:
npm ERR! npm owner ls contextify
npm ERR! There is likely additional logging output above.

npm ERR! System Windows_NT 6.1.7601
npm ERR! command "C:\Program Files\nodejs\node.exe" "C:\Program Files\nodejs\node_modules\npm\bin\npm-cli.js" "install" "juice" "--save-dev"
npm ERR! cwd D:\Projects\ModernMail\ModernMail
npm ERR! node -v v0.10.25
npm ERR! npm -v 1.3.24
npm ERR! code ELIFECYCLE
npm http 304 https://registry.npmjs.org/combined-stream
npm http 304 https://registry.npmjs.org/async
npm http GET https://registry.npmjs.org/delayed-stream/0.0.5
npm http 304 https://registry.npmjs.org/asn1/0.1.11
npm http 304 https://registry.npmjs.org/assert-plus/0.1.2
npm http 304 https://registry.npmjs.org/ctype/0.5.2
npm http 304 https://registry.npmjs.org/delayed-stream/0.0.5
npm

====npm command completed with exit code 34====

Fails on complex html document

node.js:201
        throw e; // process.nextTick error, or 'error' event on first tick
              ^
Syntax error, unrecognized expression: 

problem with node 9.3

please up dependencies of jsdom at least to 0.2.19 - to use node-gyp instead of node-waf for contextify.

Strange issue.

This:

juice.juiceContent '<!DOCTYPE html><html><head></head></body></html>', {url: \noop}, (err, html) -> console.log '\n' html

Produces this:

 <body><html><head></head></html></body>

no, I did not ad the leading/wrapping body tag. It however, is the issue.

removing the doctype declaration yields the same result:

juice.juiceContent '<html><head></head></body></html>', {url: \noop}, (err, html) -> console.log '\n' html
 <body><html><head></head></html></body>

Cannot install from npm

I was able to get juice to install a few days ago but now I get a bunch of errors. Same errors occur when using sudo and npm -g install.

npm install juice

> [email protected] preinstall /Users/ericKryski/node_modules/juice/node_modules/jsdom/node_modules/contextify
> node-waf clean || true; node-waf configure build

npm ERR! gzip "--decompress" "--stdout" "/Users/ericKryski/.npm/htmlparser/1.7.3/package.tgz" 
npm ERR! gzip "--decompress" "--stdout" "/Users/ericKryski/.npm/htmlparser/1.7.3/package.tgz" gzip: /Users/ericKryski/.npm/htmlparser/1.7.3/package.tgz: unexpected end of file
npm ERR! gzip "--decompress" "--stdout" "/Users/ericKryski/.npm/htmlparser/1.7.3/package.tgz" 
npm ERR! Failed unpacking /Users/ericKryski/.npm/htmlparser/1.7.3/package.tgz
npm ERR! error installing [email protected] Error: Failed gzip "--decompress" "--stdout" "/Users/ericKryski/.npm/htmlparser/1.7.3/package.tgz"
npm ERR! error installing [email protected] exited with 1
npm ERR! error installing [email protected]     at ChildProcess.<anonymous> (/usr/local/lib/node_modules/npm/lib/utils/exec.js:77:19)
npm ERR! error installing [email protected]     at ChildProcess.emit (events.js:67:17)
npm ERR! error installing [email protected]     at Socket.<anonymous> (child_process.js:179:12)
npm ERR! error installing [email protected]     at Socket.emit (events.js:81:20)
npm ERR! error installing [email protected]     at Array.0 (net.js:831:12)
npm ERR! error installing [email protected]     at EventEmitter._tickCallback (node.js:126:26)
npm ERR! error installing [email protected] Error: Failed gzip "--decompress" "--stdout" "/Users/ericKryski/.npm/htmlparser/1.7.3/package.tgz"
npm ERR! error installing [email protected] exited with 1
npm ERR! error installing [email protected]     at ChildProcess.<anonymous> (/usr/local/lib/node_modules/npm/lib/utils/exec.js:77:19)
npm ERR! error installing [email protected]     at ChildProcess.emit (events.js:67:17)
npm ERR! error installing [email protected]     at Socket.<anonymous> (child_process.js:179:12)
npm ERR! error installing [email protected]     at Socket.emit (events.js:81:20)
npm ERR! error installing [email protected]     at Array.0 (net.js:831:12)
npm ERR! error installing [email protected]     at EventEmitter._tickCallback (node.js:126:26)
npm ERR! error installing [email protected] Error: Failed gzip "--decompress" "--stdout" "/Users/ericKryski/.npm/htmlparser/1.7.3/package.tgz"
npm ERR! error installing [email protected] exited with 1
npm ERR! error installing [email protected]     at ChildProcess.<anonymous> (/usr/local/lib/node_modules/npm/lib/utils/exec.js:77:19)
npm ERR! error installing [email protected]     at ChildProcess.emit (events.js:67:17)
npm ERR! error installing [email protected]     at Socket.<anonymous> (child_process.js:179:12)
npm ERR! error installing [email protected]     at Socket.emit (events.js:81:20)
npm ERR! error installing [email protected]     at Array.0 (net.js:831:12)
npm ERR! error installing [email protected]     at EventEmitter._tickCallback (node.js:126:26)
npm ERR! Error: Failed gzip "--decompress" "--stdout" "/Users/ericKryski/.npm/htmlparser/1.7.3/package.tgz"
npm ERR! exited with 1
npm ERR!     at ChildProcess.<anonymous> (/usr/local/lib/node_modules/npm/lib/utils/exec.js:77:19)
npm ERR!     at ChildProcess.emit (events.js:67:17)
npm ERR!     at Socket.<anonymous> (child_process.js:179:12)
npm ERR!     at Socket.emit (events.js:81:20)
npm ERR!     at Array.0 (net.js:831:12)
npm ERR!     at EventEmitter._tickCallback (node.js:126:26)
npm ERR! Report this *entire* log at:
npm ERR!     <http://github.com/isaacs/npm/issues>
npm ERR! or email it to:
npm ERR!     <[email protected]>
npm ERR! 
npm ERR! System Darwin 11.2.0
npm ERR! command "node" "/usr/local/bin/npm" "install" "juice"
npm ERR! 
npm ERR! Additional logging details can be found in:
npm ERR!     /Users/ericKryski/Desktop/Development/MMC/npm-debug.log
npm not ok

apply body styles

Input:

<html>
  <head>
  </head>
  <body>
    <div>div1</div>
    <div>div2</div>
  </body>
</html>
body {
  font-family: sans-serif;
}

output

<div>div1</div>
<div>div2</div>

expected output

<div style="font-family: sans-serif;">div1</div>
<div style="font-family: sans-serif;">div2</div>

Preserve HTML character entity references

Juice doesn't preserve the state of your HTML character entity references when it works with documents. It makes two types of modifications:

  1. It will translate some HTML character entity references in the outputted document. For instance, &ndash; will be turned into after being passed through juice.
  2. It will do the opposite for other characters. For instance, & will become &amp;.

This behavior might be fine in most cases, but it has a number of consequences. One consequence is that opening the HTML file in a browser will display – instead of the n dash, to provide example for the 1st type of modification. Further, with my company's email system, sending the email like this will cause the email to be sent with utf-8 encoding instead of ascii, which is what it normally sends as. Independent of whether ascii is a good choice for encoding, or not, the fact that juice affects that is the principle I'm concerned with.

There would be more consistent input-output behavior if the character entity references were untouched by juice.

Edit: Upon looking at the source, this seems like an issue with JSDom. I'll be looking into the issue further there.

Why not a CLI ?

HI guys what do you think about provide a command-line interface for juice ?

So this feature can be used outside of node.js applications, will be useful no render manual templates.

If you like the idea I can make a fork.

"!important" is being stripped out

If an inline style is included with "!important", the "!important" is stripped out.

If a class contains a property with a value that is set as !important, the !important is not included when compiling the style inline.

option to remove class and id attribute

Just a nice-to-have to get a very clean HTML. As Juice strips off the links to the stylesheets, the class and id are not relevant and thus can be stripped off

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.