GithubHelp home page GithubHelp logo

bezoerb / inline-critical Goto Github PK

View Code? Open in Web Editor NEW
115.0 4.0 15.0 2.28 MB

Inline critical path CSS and async load existing stylesheets

License: Other

JavaScript 21.75% CSS 0.04% HTML 78.21%
critical-path inline css

inline-critical's Introduction

inline-critical

Inline critical-path css and load the existing stylesheets asynchronously. Existing link tags will also be wrapped in <noscript> so the users with javascript disabled will see the site rendered normally.

npm version Build Status Download Coverage Status

Installation

This module is installed via npm:

npm install inline-critical

Example Usage

const inline = require('inline-critical');
const html = fs.readFileSync('test/fixtures/index.html', 'utf8');
const critical = fs.readFileSync('test/fixtures/critical.css', 'utf8');
const inlined = inline(html, critical);

Example Usage ignoring stylesheet per regex

const inline = require('inline-critical');
const html = fs.readFileSync('test/fixtures/index.html', 'utf8');
const critical = fs.readFileSync('test/fixtures/critical.css', 'utf8');

const inlined = inline(html, critical, {
  ignore: [/bootstrap/],
});

CLI

inline-critical works well with standard input. You can either pass in the html

cat index.html | inline-critical critical.css

or just flip things around

cat critical.css | inline-critical index.html

or pass in the file as an option

inline-critical critical.css index.html

without having to worry about the correct order

inline-critical index.html critical.css

Run inline-critical --help to see the list of options.

inline(html, styles, options?)

  • html is the HTML you want to use to inline your critical styles, or any other styles
  • styles are the styles you're looking to inline
  • options is an optional configuration object
    • strategy select the preload strategy
    • extract will remove the inlined styles from any stylesheets referenced in the HTML
    • basePath will be used when extracting styles to find the files references by href attributes
    • ignore ignore matching stylesheets when inlining.
    • selector defines the element used by loadCSS as a reference for inlining.
    • noscript specifies position of noscript fallback ('body' - end of body, 'head' - end of head, false - no noscript)
    • replaceStylesheets takes an array of stylesheet hrefs to replace the original links in the document. (Used by critical when you extract uncritical css to a target file)

PreloadStrategy

The mechanism to use for lazy-loading stylesheets. [JS] indicates that a strategy requires JavaScript (falls back to <noscript>).

  • default: Move stylesheet links to the end of the document and insert preload meta tags in their place.
  • "body": Move all external stylesheet links to the end of the document.
  • "media": Load stylesheets asynchronously by adding media="print" and swap to media="all" once loaded (https://www.filamentgroup.com/lab/load-css-simpler/). [JS]
  • "swap": Convert stylesheet links to preloads that swap to rel="stylesheet" once loaded. [JS]
  • "polyfill": Inject LoadCSS and use it to load stylesheets. [JS]

Adopted from critters

License

MIT

inline-critical's People

Contributors

bevacqua avatar bezoerb avatar chrisguttandin avatar dependabot[bot] avatar georgetaveras1231 avatar greenkeeperio-bot avatar iamplugged avatar midzer avatar snyk-bot avatar thomaspaillot avatar xhmikosr 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

inline-critical's Issues

Entire <head> replaced with inlined style tag

I ran inline-critical dist/style/index.css src/index.php and in the output the entire <head> was replaced inside with the <style> tag. The output was something like this:

<?php
"some php code";
?>
<!DOCTYPE html>
<html>

<head><style>/* Inlined index.css */</style>
</head>

<body>
  <!-- The body is fine, only head was affected -->
</body>

</html>

Is there a way to run the command without replacing the entire <head>? I would like to keep my metadata:

<?php
"some php code";
?>
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">

  <meta name="description" content="Description">
  <title>Title</title>

  <style>/* Inlined index.css */</style>
</head>

<body>
  <!-- The body is fine, only head was affected -->
</body>

</html>

Integrate Stylediff?

@addyosmani I'm thinking about a way to implement the stylediff feature mentioned in addyosmani/critical#27 to inline critical.

Favored workflow at the moment:

  • When enabled, generate a diff stylesheet without the inlined styles for each of the linked css files.
  • Append a content based hash to the new Filenames filename-<hash>.css to prevent conflicts with other pages which rely on the same sylesheets.
  • Save the file next to the original one in the styles folder
  • Replace references to original file with diff'd file (noscript block as well as async function)

Do you think this is ok or can you think of a more suitable approach?
Thanks

Move to media=print async CSS loading

Working off of Critical v2.0.0-24, I'm seeing async CSS links being rewritten to a preload based strategy presumably based on the loadCSS project, but it looks like the currently recommended code favors media="print" instead of preload, and in doing so improves browser support greatly.

I think it's be worthwhile to move Critical 2.x to this print based approach, maybe optionally allowing for a declarative property to include the preload option as described further down in that article.

See addyosmani/critical#444

Print stylesheet unexpectedly gets onload attribute

Ported from addyosmani/critical#552

If I understand #444 / 07be40bab23eb518bb689062b43f0db507c9a1f9 correctly, print stylesheets should be unaffected by the script. And that seems to actually be the case—the print stylesheet is not part of the extracted critical CSS.

However, the <link> tag still erroneously gets an onload attribute, which does not make sense.

Input HTML:

<link rel="stylesheet" href="css/print.css" media="print">

Expected output:

(unchanged)

Actual output:

<link rel="stylesheet" href="css/print.css" media="print" onload="this.media='print'">

cave

Npm is reporting a security issue with an old version of lodash which is a dependency of cave which is a dependency of this project. cave was last updated 4 years ago, consider replacing it.

Incorrect handling of quotes in attributes

Seems like quotes around attributes are being normalised. This is causing problems when using single quotes:

<div data-test='"test informaiton"'>

becomes

<div data-test="" test="" informaiton""="">

Preloading strategy & uncritical css

Hi @bezoerb,

I too have to face the CSP problem, like: addyosmani/critical#480 so now I can't use the media strategy (and also swap & polyfill).

But using the other strategies I get in my html:

Using default strategy with extract=true

<head>
    <style>
        /* inlined  */
    </style>
    <link href="css/index.518624788d8bb4e2cd8b.015276a2.css" rel="preload" as="style"> /* uncritical */
</head>

<body>
    <link rel="stylesheet" href="./css/index.518624788d8bb4e2cd8b.css">  /* original css */
</body>

Problems: the css classes are duplicated, because the original stylesheet contains inlined & uncritical classes.

and generates in Chrome the message: The resource http://localhost:8080/css/index.518624788d8bb4e2cd8b.015276a2.css was preloaded using link preload but not used within a few seconds from the window's load event. Please make sure it has an appropriate as value and it is preloaded intentionally.

Using body strategy with extract=true

<head>
    <style>
        /* inlined  */
    </style>
    /* missing uncritical */
</head>

<body>
    <link rel="stylesheet" href="./css/index.518624788d8bb4e2cd8b.css">  /* original css */
</body>

Problems: uncritical file is missing, and inlined css classes are duplicated, because the original stylesheet contains inlined classes.

Expected behavior
Even if asynchronous loading of css doesn't work with CSP, I would at least get the inlining and loading of uncritical css, for example:

<head>
    <style>
        /* inlined  */
    </style>
    <link rel="stylesheet" href="css/index.518624788d8bb4e2cd8b.015276a2.css"> /* uncritical */
</head>

<body>
 /* no original css */
</body>

But at the moment this does not seem possible, and I have to give up using extract=true.

Thanks

Several warnings appear when installing the latest version of critical

critical -> inline-critical -> jsdom: WARN  deprecated [email protected]: Use your platform's native DOMException instead
critical -> inline-critical -> jsdom -> data-urls: WARN  deprecated [email protected]: Use your platform's native atob() and btoa() methods instead
critical -> inline-critical -> jsdom: WARN  deprecated [email protected]: Use your platform's native atob() and btoa() methods instead

See: addyosmani/critical#585

Another SVG issue

eg:

<polygon fill="#DD7B27" points="55.92,7 91.96,61.76 20.24,61.76 "/>

Becomes:

<polygon fill="#DD7B27" points="55.92,7 91.96,61.76 20.24,61.76 "></polygon>

Optional loadCSS

I think it would be great if the inline-critical takes an optional parameter to whether or not inject the loadCSS function. This way the user of the module has more control of how they want to load the stylesheets.
I wanted to open an issue to see if this aligns with what the module is intended to do before I submit a pull request.

Failed to inline when there is no link, style or script tag.

When the HTML provided has not link, style or script tag it fails to inline the CSS. I checked to --selector option, but I didn't find a way to especify "before close head (before )".
When there is not link, style or script tag, just insert it before closing head tag</head>

loadCSS is not defined when `global` is available

Hi

After upgrading inline-critical to 1.0.0 we started receiving the loadCSS is not defined error.

We are running an app using nw.js. When inspected global exists, so loadCSS gets assigned to it. This said it doesn't make it available globally and I don't think it's the object expected by inline-critical. We temporally overcame this issue by overwriting global variable to point to window instance in the scope of the function that defines the loadCSS.

An insights ?

Bump loadcss

Bump loadcss to the latest version.
Implies changing the tests here as well as the inline tests for critical

`replaceStylesheets` question

I was looking at this code @bezoerb:

if (Array.isArray(o.replaceStylesheets) > 0 && links.length > 0) {

Array.isArray(o.replaceStylesheets) > 0 seems weird. Maybe we should expand the check to make it clearer?

Maybe:

!Boolean(o.replaceStylesheets) && Array.isArray(o.replaceStylesheets) && o.replaceStylesheets.length> 0

Or

!Boolean(o.replaceStylesheets) && o.replaceStylesheets.length> 0

PS. I hope I'm not overthinking this, it just seems weird to me :)
PPS. not sure if this affects the 4.x branch

<noscript>'s to the bottom of HTML page?

Could this be an option to inject <noscript>s with stylesheet links to the bottom of the html page (in front of </body>)? Why? Because Google PageSpeed still complains about css files that are blocking even if they are in noscripts..

Can't change "media" attr in <link>

Inside loadCSS.js the library always output "media='only x'" when the styles are loaded async. Would be really useful if the script could read the "media" attribute placed in the html like:

<link rel="stylesheet" href="styles.css" media="all">

and output:

// ...
o.rel="stylesheet",
o.href=e,
o.media="all", /* instead of "only x" */
i.parentNode.insertBefore(o,i)
// ...

npm3 vs. reading `loadcss` file

Hi, just wanted to let you know that this line didn't work for me using npm3.

Because npm3 tries to make the node_modules folder flat, the fg-loadcss module isn't in inline-critical/node_modules/fg-loadcss/ anymore.

5.x performance and size

I was wondering, do we really need prettier? It's not a small dependency... https://packagephobia.now.sh/result?p=prettier

And this affects not only download size (https://packagephobia.now.sh/result?p=inline-critical) but also module load time. On my machine v4.x loads significantly faster:

C:\Users\xmr\Desktop\inline-critical>git branch
  master
* v4.x

C:\Users\xmr\Desktop\inline-critical>node load-time.js
Load time
Module: 209.547ms

C:\Users\xmr\Desktop\inline-critical>node load-time.js
Load time
Module: 198.942ms

C:\Users\xmr\Desktop\inline-critical>node load-time.js
Load time
Module: 200.581ms

C:\Users\xmr\Desktop\inline-critical>node load-time.js
Load time
Module: 179.488ms

C:\Users\xmr\Desktop\inline-critical>git branch
* master
  v4.x

C:\Users\xmr\Desktop\inline-critical>node load-time.js
Load time
Module: 713.050ms

C:\Users\xmr\Desktop\inline-critical>node load-time.js
Load time
Module: 702.322ms

C:\Users\xmr\Desktop\inline-critical>node load-time.js
Load time
Module: 715.274ms

C:\Users\xmr\Desktop\inline-critical>node load-time.js
Load time
Module: 684.928ms

The slowdown isn't only from prettier, but could be from jsdom too.

Why can't we just not minify the critical CSS if minify: false? It should help a bit.

document.querySelector throws error with bad selector

This library is being run via rollup-plugin-critical -> critical -> inline-critical

When trying to generate an inline stylesheet, the code falls over at https://github.com/bezoerb/inline-critical/blob/main/index.js#L80-L81 with an error of:

 ERROR  'noscript)>[rel="stylesheet"]' is not a valid selector

  at emit (node_modules/nwsapi/src/nwsapi.js:557:17)
  at Object._matches [as match] (node_modules/nwsapi/src/nwsapi.js:1400:9)
  at Array.Resolver (eval at compile (node_modules/nwsapi/src/nwsapi.js:760:17), <anonymous>:3:105)
  at collect (node_modules/nwsapi/src/nwsapi.js:1552:21)
  at _querySelectorAll (node_modules/nwsapi/src/nwsapi.js:1509:36)
  at Object._querySelector [as first] (node_modules/nwsapi/src/nwsapi.js:1415:14)
  at DocumentImpl.querySelector (node_modules/inline-critical/node_modules/jsdom/lib/jsdom/living/nodes/ParentNode-impl.js:69:44)
  at Document.querySelector (node_modules/inline-critical/node_modules/jsdom/lib/jsdom/living/generated/Document.js:1026:58)
  at Dom.querySelector (node_modules/inline-critical/src/dom.js:155:26)
  at inline (node_modules/inline-critical/index.js:85:27)

This isn't the selector being passed in (that would be :not(noscript) > [rel="stylesheet"]) but somewhere around the nwsapi it gets mangled and comes out as a bad selector.

If I comment out these selectors in the code, it works. Adding a selector as an option has no effect as all selectors are evaluated.

Go templates support

RE addyosmani/critical#180

My Go templates like

<li>Version: <a href="https://github.com/kaihendry/ltabus/commit/{{ getenv "UP_COMMIT" }}">{{ getenv "UP_COMMIT" }}</a></li>

get messed up to:

<li>Version: <a href="https://github.com/kaihendry/ltabus/commit/{{ getenv " up_commit"="" }}"="">{{ getenv "UP_COMMIT" }}</a></li>

Whilst using npx critical --inline templates/index.html.in > templates/index.html.

Let me know if I can help.

Extract can't find files that are built with a public path

Scenario: You have a SPA, say a VueJS one built with Vite. This app is to be deployed on a public path, say myapp so it'd be www.example.com/myapp/.

When the SPA bundle is built, its assets links are of the format /myapp/assets/main.css, but in the dist directory, the build is just dist/assets/main.css

It doesn't appear to be possible to get the extract option of inline-critical to function. https://github.com/bezoerb/inline-critical/blob/main/index.js#L165 is resolve(join(o.basePath || process.cwd, href)); which, with a basePath of "./dist" resolves to /directory/dist/myapp/assets/main.css which is incorrect.

It would be great if we could specify a publicPath option that lets inline-critical know that there's a public path parameter that it should ignore/remove when trying to locate the CSS file.

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.