metalsmith-inline-critical-css
Advanced tools
Comparing version 1.0.0-beta to 1.0.0-beta.2
"use strict"; | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
}; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
@@ -33,59 +24,52 @@ return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
invariant_1.default(!!cssFile && !Array.isArray(cssFile), 'You must supply a single css file to look for. Multiple files are not currently supported.'); | ||
return function (files, metalsmith, done) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
debug('WILL: Read CSS file', cssFile); | ||
const cssFilePath = path_1.default.resolve(cssFile); | ||
const cssContent = fs_1.default.readFileSync(cssFilePath, { encoding: 'utf-8' }); | ||
debug('OK: Read CSS file'); | ||
// Read the LoadCSS file once | ||
debug('WILL: Read LoadCSS contents'); | ||
const loadCssPreloadContent = getLoadCSSFallback(); | ||
debug('OK: Read LoadCSS contents'); | ||
// Loop over all the files, applying the transform if matching | ||
yield Promise.all(Object.keys(files) | ||
.filter(file => { | ||
const matches = multimatch_1.default(file, pattern).length > 0; | ||
if (matches) { | ||
debug(`MATCH: ${file}`); | ||
} | ||
else { | ||
debug(`NO MATCH: ${file}`); | ||
} | ||
return matches; | ||
}) | ||
.map(function (file) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
debug(`WILL: Run for ${file}`); | ||
// utf-8 decode read file contents | ||
debug('WILL: Read html file contents'); | ||
let fileContent = files[file].contents; | ||
if (!!fileContent && fileContent instanceof Buffer) { | ||
fileContent = fileContent.toString('utf-8'); | ||
} | ||
debug('OK: Read html file contents'); | ||
debug('WILL: get used CSS'); | ||
const usedCss = yield getUsedCss({ | ||
htmlContent: fileContent, | ||
cssContent: cssContent, | ||
}); | ||
debug('OK: get used CSS'); | ||
// NOTE: The gzip size will be even smaller when inlined into the document, | ||
// because the classes are shared | ||
debug(`Used CSS gzip-size (standalone): ${gzip_size_1.default.sync(usedCss)} B`); | ||
debug('WILL: inject used CSS to file contents'); | ||
const htmlWithInline = inlineCriticalCss({ | ||
htmlContent: fileContent, | ||
cssPublicPath: cssPublicPath, | ||
criticalCssContent: usedCss, | ||
loadCssPreloadContent, | ||
}); | ||
debug('OK: inject used CSS to file contents'); | ||
debug('WILL: write to file contents'); | ||
files[file].contents = htmlWithInline; | ||
debug('OK: write to file contents'); | ||
debug('OK: Critical CSS plugin run'); | ||
}); | ||
})); | ||
done(); | ||
}); | ||
return async function (files, metalsmith, done) { | ||
debug('WILL: Read CSS file', cssFile); | ||
const cssFilePath = path_1.default.resolve(cssFile); | ||
const cssContent = fs_1.default.readFileSync(cssFilePath, { encoding: 'utf-8' }); | ||
debug({ cssFilePath, cssContent }); | ||
debug('OK: Read CSS file'); | ||
// Loop over all the files, applying the transform if matching | ||
await Promise.all(Object.keys(files) | ||
.filter(file => { | ||
const matches = multimatch_1.default(file, pattern).length > 0; | ||
if (matches) { | ||
debug(`MATCH: ${file}`); | ||
} | ||
else { | ||
debug(`NO MATCH: ${file}`); | ||
} | ||
return matches; | ||
}) | ||
.map(async function (file) { | ||
debug(`WILL: Run for ${file}`); | ||
// utf-8 decode read file contents | ||
debug('WILL: Read html file contents'); | ||
let fileContent = files[file].contents; | ||
if (!!fileContent && fileContent instanceof Buffer) { | ||
fileContent = fileContent.toString('utf-8'); | ||
} | ||
debug('OK: Read html file contents'); | ||
debug('WILL: get used CSS'); | ||
debug({ fileContent, cssContent }); | ||
const usedCss = await getUsedCss({ | ||
htmlContent: fileContent, | ||
cssContent: cssContent, | ||
}); | ||
debug('OK: get used CSS'); | ||
// NOTE: The gzip size will be even smaller when inlined into the document, | ||
// because the classes are shared | ||
debug(`Used CSS gzip-size (standalone): ${gzip_size_1.default.sync(usedCss)} B`); | ||
debug('WILL: inject used CSS to file contents'); | ||
const htmlWithInline = inlineCriticalCss({ | ||
htmlContent: fileContent, | ||
cssPublicPath: cssPublicPath, | ||
criticalCssContent: usedCss, | ||
}); | ||
debug('OK: inject used CSS to file contents'); | ||
debug('WILL: write to file contents'); | ||
files[file].contents = htmlWithInline; | ||
debug('OK: write to file contents'); | ||
debug('OK: Critical CSS plugin run'); | ||
})); | ||
done(); | ||
}; | ||
@@ -99,4 +83,4 @@ } | ||
*/ | ||
const getUsedCss = ({ htmlContent, cssContent }) => __awaiter(void 0, void 0, void 0, function* () { | ||
const purgeCssResults = yield new purgecss_1.default().purge({ | ||
const getUsedCss = async ({ htmlContent, cssContent }) => { | ||
const purgeCssResults = await new purgecss_1.default().purge({ | ||
content: [ | ||
@@ -124,3 +108,3 @@ { | ||
return usedCss; | ||
}); | ||
}; | ||
/** | ||
@@ -131,3 +115,3 @@ * Inline Critical CSS in HTML, by replacing <link rel="stylesheet"> | ||
*/ | ||
function inlineCriticalCss({ htmlContent, cssPublicPath, criticalCssContent, loadCssPreloadContent, }) { | ||
function inlineCriticalCss({ htmlContent, cssPublicPath, criticalCssContent, }) { | ||
// Set up new markup | ||
@@ -144,25 +128,16 @@ const criticalStyleTag = `<style>${criticalCssContent}</style>`; | ||
} | ||
// NOTE: .html() returns '' for some reason, so we use toString() instead... | ||
const linkStylesheet = $link.toString(); | ||
const noscriptFallback = `<noscript>${linkStylesheet}</noscript>`; | ||
// Fallback for browsers that do not support link rel="preload" | ||
// @see https://github.com/filamentgroup/loadCSS | ||
const loadCssPreloadScript = `<script>${loadCssPreloadContent}</script>`; | ||
/* Add the relevant markup to the page. | ||
/* Change the link tag and add the relevant markup to the page. | ||
* <link rel="stylesheet"...> -> | ||
* <style>.inlined-things{...}</style> | ||
* <link rel="preload" href="path/to/mystylesheet.css" as="style" onload="this.rel='stylesheet'"> | ||
* <link rel="stylesheet" href="/path/to/mystylesheet.css" media="print" onload="this.media='all'"> | ||
* <noscript><link rel="stylesheet" ...></noscript> | ||
* <script>load-css-preload-polyfill</script> | ||
*/ | ||
$link | ||
// @ts-ignore | ||
.attr({ | ||
rel: 'preload', | ||
as: 'style', | ||
// eslint-disable-next-line quotes | ||
onload: `this.onload=null;this.rel='stylesheet'`, | ||
media: 'print', | ||
onload: `this.media='all'`, | ||
}) | ||
.before(criticalStyleTag) | ||
.after(loadCssPreloadScript) | ||
.after(noscriptFallback); | ||
@@ -172,10 +147,2 @@ const newHtml = $.html(); | ||
} | ||
/** | ||
* Inlined cssrelpreload.min.js fallback | ||
*/ | ||
function getLoadCSSFallback() { | ||
return ` | ||
!function(t){"use strict";t.loadCSS||(t.loadCSS=function(){});var e=loadCSS.relpreload={};if(e.support=function(){var e;try{e=t.document.createElement("link").relList.supports("preload")}catch(t){e=!1}return function(){return e}}(),e.bindMediaToggle=function(t){function e(){t.media=a}var a=t.media||"all";t.addEventListener?t.addEventListener("load",e):t.attachEvent&&t.attachEvent("onload",e),setTimeout(function(){t.rel="stylesheet",t.media="only x"}),setTimeout(e,3e3)},e.poly=function(){if(!e.support())for(var a=t.document.getElementsByTagName("link"),n=0;n<a.length;n++){var o=a[n];"preload"!==o.rel||"style"!==o.getAttribute("as")||o.getAttribute("data-loadcss")||(o.setAttribute("data-loadcss",!0),e.bindMediaToggle(o))}},!e.support()){e.poly();var a=t.setInterval(e.poly,500);t.addEventListener?t.addEventListener("load",function(){e.poly(),t.clearInterval(a)}):t.attachEvent&&t.attachEvent("onload",function(){e.poly(),t.clearInterval(a)})}"undefined"!=typeof exports?exports.loadCSS=loadCSS:t.loadCSS=loadCSS}("undefined"!=typeof global?global:this); | ||
`; | ||
} | ||
//# sourceMappingURL=index.js.map |
{ | ||
"name": "metalsmith-inline-critical-css", | ||
"version": "1.0.0-beta", | ||
"version": "1.0.0-beta.2", | ||
"description": "Metalsmith plugin to inspect HTML files, inline used selectors from specified CSS, and load the rest asynchronously.", | ||
@@ -47,2 +47,3 @@ "main": "dist/index.js", | ||
"@types/jest": "^25.1.2", | ||
"@types/node": "^13.7.2", | ||
"husky": "^4.2.3", | ||
@@ -49,0 +50,0 @@ "jest": "^25.1.0", |
Sorry, the diff of this file is not supported yet
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
11
523
27609
14
2