Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: minifyConditionalComments #119

Merged
merged 5 commits into from
Nov 12, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,49 @@ Minified:
<svg baseProfile="full" width="300" height="200" xmlns="http://www.w3.org/2000/svg"><rect width="100%" height="100%" fill="red"/><circle cx="150" cy="100" r="80" fill="green"/><text x="150" y="125" font-size="60" text-anchor="middle" fill="#fff">SVG</text></svg>
```

### minifyConditionalComments

Minify content inside conditional comments.

##### Example

Source:

```html
<!--[if lte IE 7]>
<style type="text/css">
.title {
color: red;
}
</style>
<![endif]-->
```

Minified:

```html
<!--[if lte IE 7]><style>.title {color:red}</style><![endif]-->
```

##### Notice

Due to [the limitation of PostHTML](https://github.com/posthtml/posthtml-parser/issues/9) (which is actually a issue from upstream [htmlparser2](https://github.com/fb55/htmlparser2/pull/146)), following html snippet is not supported:

```html
<!--[if lt IE 7]><html class="no-js ie6"><![endif]-->
<!--[if IE 7]><html class="no-js ie7"><![endif]-->
<!--[if IE 8]><html class="no-js ie8"><![endif]-->
<!--[if gt IE 8]><!--><html class="no-js"><!--<![endif]-->
```

Which will result in:

```html
<!--[if lt IE 7]><html class="no-js ie6"></html><![endif]-->
<!--[if IE 7]><html class="no-js ie7"></html><![endif]-->
<!--[if IE 8]><html class="no-js ie8"></html><![endif]-->
<!--[if gt IE 8]><!--><html class="no-js"></html><!--<![endif]-->
```

### removeRedundantAttributes
Removes redundant attributes from tags if they contain default values:
Expand Down
44 changes: 44 additions & 0 deletions lib/modules/minifyConditionalComments.es6
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import htmlnano from '../htmlnano';
import { isConditionalComment } from '../helpers';

// Spec: https://docs.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/compatibility/ms537512(v=vs.85)
const CONDITIONAL_COMMENT_REGEXP = /(<!--\[if\s+?[^<>[\]]+?]>)([\s\S]+?)(<!\[endif\]-->)/gm;

/** Minify content inside conditional comments */
export default async function minifyConditionalComments(tree, htmlnanoOptions) {
// forEach, tree.walk, tree.match just don't support Promise.
for (let i = 0, len = tree.length; i < len; i++) {
Comment on lines +9 to +10
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I mentioned in #16 (comment), tree.walk & tree.match is not designed to be work with Promise in the first place. Thus I use for loop to traverse through tree.

const node = tree[i];

if (typeof node === 'string' && isConditionalComment(node)) {
tree[i] = await minifycontentInsideConditionalComments(node, htmlnanoOptions);
}

if (node.content && node.content.length) {
tree[i].content = await minifyConditionalComments(node.content, htmlnanoOptions);
}
}

return tree;
}

async function minifycontentInsideConditionalComments(text, htmlnanoOptions) {
let match;
const matches = [];

// FIXME!
// String#matchAll is supported since Node.js 12
while ((match = CONDITIONAL_COMMENT_REGEXP.exec(text)) !== null) {
matches.push([match[1], match[2], match[3]]);
}

if (!matches.length) {
return Promise.resolve(text);
}

return Promise.all(matches.map(async match => {
const result = await htmlnano.process(match[1], htmlnanoOptions, {}, {});

return match[0] + result.html + match[2];
}));
}
1 change: 1 addition & 0 deletions lib/presets/max.es6
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@ export default { ...safePreset,
preset: 'default',
},
minifySvg: {},
minifyConditionalComments: true,
removeOptionalTags: true,
};
1 change: 1 addition & 0 deletions lib/presets/safe.es6
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export default {
{ convertShapeToPath: false },
],
},
minifyConditionalComments: false,
removeEmptyAttributes: true,
removeRedundantAttributes: false,
removeComments: 'safe',
Expand Down
80 changes: 80 additions & 0 deletions test/modules/minifyConditionalComments.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { init } from '../htmlnano';
import safePreset from '../../lib/presets/safe';


describe('minifyConditionalComments', () => {
const fixture = {
fullHtml: `
<!DOCTYPE html>
<html class="no-js">
<head>
<meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible">
<meta charset="utf-8">
<!--[if lte IE 7]>
<style type="text/css">
.title {
color: red;
}
</style>
<![endif]-->
</head>
</html>`,
fullHtmlMinified: '<!DOCTYPE html><html class="no-js"><head><meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible"><meta charset="utf-8"><!--[if lte IE 7]><style type="text/css">.title{color:red}</style><![endif]--></head></html>',
multipleConditionalComment: `
<!--[if lt IE 7 ]>
<div class="ie6">
</div>
<![endif]-->
<!--[if IE 7 ]>
<div class="ie7">
</div>
<![endif]-->
<!--[if IE 8 ]>
<div class="ie8">
</div>
<![endif]-->
<!--[if IE 9 ]>
<div class="ie9">
</div>
<![endif]-->
<!--[if (gt IE 9)|!(IE)]><!-->
<div class="w3c">
</div>
<!--<![endif]-->`,
multipleConditionalCommentMinified: '<!--[if lt IE 7 ]><div class="ie6"> </div><![endif]--><!--[if IE 7 ]><div class="ie7"> </div><![endif]--><!--[if IE 8 ]><div class="ie8"> </div><![endif]--><!--[if IE 9 ]><div class="ie9"> </div><![endif]--><!--[if (gt IE 9)|!(IE)]><!--><div class="w3c"> </div>',
singleLineMultipleConditionalComment: `
<!--[if lt IE 7 ]><div class="ie6"></div><![endif]--><!--[if IE 7 ]><div class="ie7"></div><![endif]--><!--[if IE 8 ]><div class="ie8"></div><![endif]--><!--[if IE 9 ]><div class="ie9"></div><![endif]--><!--[if (gt IE 9)|!(IE)]><!--><div class="w3c"></div><!--<![endif]-->`,
};

it('common html', () => {
return init(
fixture.fullHtml,
fixture.fullHtmlMinified,
{
...safePreset,
minifyConditionalComments: true
}
);
});

it('multiple conditional comment', () => {
return init(
fixture.multipleConditionalComment,
fixture.multipleConditionalCommentMinified,
{
...safePreset,
minifyConditionalComments: true
}
);
});

it('multiple conditional comment in single line', () => {
return init(
fixture.singleLineMultipleConditionalComment,
fixture.singleLineMultipleConditionalComment,
{
minifyConditionalComments: true
}
);
});
});