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

Add exactSelector option to match only provided selector #280

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
33 changes: 19 additions & 14 deletions cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,19 @@ const help = `
Usage: inline-critical <input> [<option>]

Options:
-c, --css Path to CSS file
-h, --html Path to HTML file
-i, --ignore Skip matching stylesheets
-m, --minify Minify the styles before inlining (default)
-p, --preload Adds preload tags

-e, --extract Remove the inlined styles from any stylesheets referenced in the HTML
-b, --base Is used when extracting styles to find the files references by href attributes
-s, --selector Optionally defines the element used by loadCSS as a reference for inlining

--polyfill Use loadCSS polyfill instead of media=print
--noscript Position of noscript fallback ('body' - end of body, 'head' - end of head, false - no noscript)
-c, --css Path to CSS file
-h, --html Path to HTML file
-i, --ignore Skip matching stylesheets
-m, --minify Minify the styles before inlining (default)
-p, --preload Adds preload tags

-e, --extract Remove the inlined styles from any stylesheets referenced in the HTML
-b, --base Is used when extracting styles to find the files references by href attributes
-s, --selector Optionally defines the element used by loadCSS as a reference for inlining

--exact-selector Match against only the provided selector as a reference for inlining
--polyfill Use loadCSS polyfill instead of media=print
--noscript Position of noscript fallback ('body' - end of body, 'head' - end of head, false - no noscript)
`;

const cli = meow(help, {
Expand All @@ -54,6 +55,11 @@ const cli = meow(help, {
alias: 'm',
default: true,
},
preload: {
type: 'boolean',
alias: 'p',
default: false,
},
extract: {
type: 'boolean',
alias: 'e',
Expand All @@ -66,9 +72,8 @@ const cli = meow(help, {
type: 'string',
alias: 's',
},
preload: {
exactSelector: {
type: 'boolean',
alias: 'p',
default: false,
},
polyfill: {
Expand Down
21 changes: 15 additions & 6 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,21 @@ function inline(html, styles, options) {
return stylesheet && !o.ignore.some((i) => (isRegExp(i) && i.test(href)) || i === href);
});

const targetSelectors = [
o.selector,
':not(noscript) > link[rel="stylesheet"]',
':not(noscript) > link[rel="preload"][as="style"]',
'head script',
];
let targetSelectors;
if (o.exactSelector) {
if (!o.selector) {
throw new Error(`Error: exactSelector option provided but no selector provided. Specify selector.`);
}

targetSelectors = [o.selector];
} else {
targetSelectors = [
o.selector,
':not(noscript) > link[rel="stylesheet"]',
':not(noscript) > link[rel="preload"][as="style"]',
'head script',
];
}

const target = document.querySelector(targetSelectors);
const inlined = `${inlineStyles}\n${missingStyles}`;
Expand Down
2 changes: 2 additions & 0 deletions test/cli.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ describe('Mocked', () => {
'--minify',
'--extract',
'--preload',
'--exactSelector',
'--polyfill',
'--noscript',
'head',
Expand All @@ -150,6 +151,7 @@ describe('Mocked', () => {
minify: true,
extract: true,
preload: true,
exactSelector: true,
polyfill: true,
noscript: 'head',
});
Expand Down
60 changes: 60 additions & 0 deletions test/expected/index-script-before-exact.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<!doctype html>
<html class="no-js">
<head>
<meta charset="utf-8">
<title>critical css test</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Place favicon.ico and apple-touch-icon.png in the root directory -->

<script>document.documentElement.classList.remove("no-js")</script>

<!-- build:css styles/main.css -->
<style>
body{padding-top:20px;padding-bottom:20px}.header{padding-left:15px;padding-right:15px}.header{border-bottom:1px solid #e5e5e5}.header h3{margin-top:0;margin-bottom:0;line-height:40px;padding-bottom:19px}.jumbotron{text-align:center;border-bottom:1px solid #e5e5e5}.jumbotron .btn{font-size:21px;padding:14px 24px}@media screen and (min-width:768px){.container{max-width:730px}.header{padding-left:0;padding-right:0}.header{margin-bottom:30px}.jumbotron{border-bottom:0}}html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}a{background:0 0}h1{margin:.67em 0;font-size:2em}@media print{*{color:#000!important;text-shadow:none!important;background:0 0!important;box-shadow:none!important}a{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}a[href^="#"]:after{content:""}h3,p{orphans:3;widows:3}h3{page-break-after:avoid}}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:62.5%;-webkit-tap-highlight-color:transparent}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}a{color:#428bca;text-decoration:none}h1,h3{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}h1,h3{margin-top:20px;margin-bottom:10px}h1{font-size:36px}h3{font-size:24px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:200;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}.text-muted{color:#999}ul{margin-top:0;margin-bottom:10px}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-lg{padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a{color:#fff;background-color:#428bca}.jumbotron{padding:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.container .jumbotron{border-radius:6px}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron{padding-right:60px;padding-left:60px}.jumbotron h1{font-size:63px}}.container:after,.container:before,.nav:after,.nav:before{display:table;content:" "}.container:after,.nav:after{clear:both}.pull-right{float:right!important}
</style>
<link rel="preload" href="css/main.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<link rel="preload" href="bower_components/bootstrap/dist/css/bootstrap.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<script>!function(n){"use strict";n.loadCSS||(n.loadCSS=function(){});var t,o=loadCSS.relpreload={};o.support=function(){var e;try{e=n.document.createElement("link").relList.supports("preload")}catch(t){e=!1}return function(){return e}}(),o.bindMediaToggle=function(t){var e=t.media||"all";function a(){t.addEventListener?t.removeEventListener("load",a):t.attachEvent&&t.detachEvent("onload",a),t.setAttribute("onload",null),t.media=e}t.addEventListener?t.addEventListener("load",a):t.attachEvent&&t.attachEvent("onload",a),setTimeout(function(){t.rel="stylesheet",t.media="only x"}),setTimeout(a,3e3)},o.poly=function(){if(!o.support())for(var t=n.document.getElementsByTagName("link"),e=0;e<t.length;e++){var a=t[e];"preload"!==a.rel||"style"!==a.getAttribute("as")||a.getAttribute("data-loadcss")||(a.setAttribute("data-loadcss",!0),o.bindMediaToggle(a))}},o.support()||(o.poly(),t=n.setInterval(o.poly,500),n.addEventListener?n.addEventListener("load",function(){o.poly(),n.clearInterval(t)}):n.attachEvent&&n.attachEvent("onload",function(){o.poly(),n.clearInterval(t)})),"undefined"!=typeof exports?exports.loadCSS=loadCSS:n.loadCSS=loadCSS}("undefined"!=typeof global?global:this);</script>
<!-- endbuild -->

</head>
<body>
<!--[if lt IE 10]>
<p class="browsehappy">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
<![endif]-->

<div class="container">
<div class="header">
<ul class="nav nav-pills pull-right">
<li class="active"><a href="#">Home</a></li>
<li><a href="#">About</a></li>
<li><a href="#">Contact</a></li>
</ul>
<h3 class="text-muted">critical css test</h3>
</div>

<div class="jumbotron">
<h1>'Allo, 'Allo!</h1>
<p class="lead">Always a pleasure scaffolding your apps.</p>
<p><a class="btn btn-lg btn-success" href="#">Splendid!</a></p>
</div>

<div class="row marketing">
<div class="col-lg-6">
<h4>HTML5 Boilerplate</h4>
<p>HTML5 Boilerplate is a professional front-end template for building fast, robust, and adaptable web apps or sites.</p>

<h4>Bootstrap</h4>
<p>Sleek, intuitive, and powerful mobile first front-end framework for faster and easier web development.</p>
</div>
</div>

<div class="footer">
<p>♥ from the Yeoman team</p>
</div>
</div>
<noscript><link rel="stylesheet" href="css/main.css"></noscript>
<noscript><link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.css"></noscript>
</body>
</html>
54 changes: 54 additions & 0 deletions test/fixtures/index-script.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<!doctype html>
<html class="no-js">
<head>
<meta charset="utf-8">
<title>critical css test</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Place favicon.ico and apple-touch-icon.png in the root directory -->

<script>document.documentElement.classList.remove("no-js")</script>

<!-- build:css styles/main.css -->
<link rel="stylesheet" href="css/main.css">
<link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.css" />
<!-- endbuild -->

</head>
<body>
<!--[if lt IE 10]>
<p class="browsehappy">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
<![endif]-->

<div class="container">
<div class="header">
<ul class="nav nav-pills pull-right">
<li class="active"><a href="#">Home</a></li>
<li><a href="#">About</a></li>
<li><a href="#">Contact</a></li>
</ul>
<h3 class="text-muted">critical css test</h3>
</div>

<div class="jumbotron">
<h1>'Allo, 'Allo!</h1>
<p class="lead">Always a pleasure scaffolding your apps.</p>
<p><a class="btn btn-lg btn-success" href="#">Splendid!</a></p>
</div>

<div class="row marketing">
<div class="col-lg-6">
<h4>HTML5 Boilerplate</h4>
<p>HTML5 Boilerplate is a professional front-end template for building fast, robust, and adaptable web apps or sites.</p>

<h4>Bootstrap</h4>
<p>Sleek, intuitive, and powerful mobile first front-end framework for faster and easier web development.</p>
</div>
</div>

<div class="footer">
<p>♥ from the Yeoman team</p>
</div>
</div>
</body>
</html>
13 changes: 13 additions & 0 deletions test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,19 @@ test('Respect selector option (with media=print)', async () => {
expect(strip(out.toString('utf-8'))).toBe(strip(expected));
});

test('Respect selector option (with exactSelector option)', async () => {
const html = await read('fixtures/index-script.html');
const css = await read('fixtures/critical.css');

const expected = await read('expected/index-script-before-exact.html');
const out = inline(html, css, {
polyfill: true,
selector: 'link[rel="stylesheet"]',
exactSelector: true,
});

expect(strip(out.toString('utf-8'))).toBe(strip(expected));
});
test('Ignore stylesheets wrapped in noscript', async () => {
const html = await read('fixtures/index-noscript.html');
const css = await read('fixtures/critical.css');
Expand Down