├── src ├── __tests__ │ ├── inline-false │ │ ├── style.css │ │ ├── __snapshots__ │ │ │ └── inline-false.test.ts.snap │ │ ├── index.html │ │ └── inline-false.test.ts │ └── inline-true │ │ ├── style.css │ │ ├── index.html │ │ ├── __snapshots__ │ │ └── inline-true.test.ts.snap │ │ └── inline-true.test.ts ├── @types │ ├── rollup-plugin-critical.d.ts │ ├── penthouse.d.ts │ └── critical.d.ts └── index.ts ├── vitest.config.ts ├── Dockerfile ├── dist ├── index.d.ts ├── index.d.cts ├── index.js ├── index.cjs ├── index.js.map └── index.cjs.map ├── tsconfig.json ├── LICENSE.md ├── eslint.config.js ├── package.json ├── Makefile ├── CHANGELOG.md └── README.md /src/__tests__/inline-false/style.css: -------------------------------------------------------------------------------- 1 | .heading { 2 | color: red; 3 | } 4 | 5 | .unused-class { 6 | border: 3px solid blue; 7 | } 8 | -------------------------------------------------------------------------------- /src/__tests__/inline-true/style.css: -------------------------------------------------------------------------------- 1 | .heading { 2 | color: red; 3 | } 4 | 5 | .unused-class { 6 | border: 3px solid blue; 7 | } 8 | -------------------------------------------------------------------------------- /vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitest/config' 2 | 3 | export default defineConfig({ 4 | test: { 5 | testTimeout: 20000, 6 | }, 7 | }); 8 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | ARG TAG=22-alpine 2 | FROM nystudio107/node-dev-base:$TAG 3 | 4 | WORKDIR /app/ 5 | 6 | RUN npm install -g npm@^11.0.0 7 | 8 | CMD ["run build"] 9 | 10 | ENTRYPOINT ["npm"] 11 | -------------------------------------------------------------------------------- /src/__tests__/inline-false/__snapshots__/inline-false.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`\`inline: false\` Critical CSS generation 1`] = `".heading{color:red}"`; 4 | -------------------------------------------------------------------------------- /src/__tests__/inline-false/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Critical CSS test 4 | 5 | 6 | 7 |

8 | hello, world. 9 |

10 | 11 | 12 | -------------------------------------------------------------------------------- /src/__tests__/inline-true/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Critical CSS test 4 | 5 | 6 | 7 |

8 | hello, world. 9 |

10 | 11 | 12 | -------------------------------------------------------------------------------- /dist/index.d.ts: -------------------------------------------------------------------------------- 1 | import { Plugin } from 'rollup'; 2 | 3 | /** 4 | * [Vite.js](https://vitejs.dev/) & [Rollup](https://rollupjs.org/) plugin for generating critical CSS 5 | * that uses the [critical](https://github.com/addyosmani/critical) generator under the hood. 6 | * 7 | * @param {CriticalPluginConfig} pluginConfig - the plugin configuration object 8 | * @param {Function} callback - callback upon completion of the critical CSS generation 9 | * @constructor 10 | */ 11 | declare function PluginCritical(pluginConfig: CriticalPluginConfig, callback?: CriticalPluginCallback): Plugin; 12 | 13 | export { PluginCritical as default }; 14 | -------------------------------------------------------------------------------- /dist/index.d.cts: -------------------------------------------------------------------------------- 1 | import { Plugin } from 'rollup'; 2 | 3 | /** 4 | * [Vite.js](https://vitejs.dev/) & [Rollup](https://rollupjs.org/) plugin for generating critical CSS 5 | * that uses the [critical](https://github.com/addyosmani/critical) generator under the hood. 6 | * 7 | * @param {CriticalPluginConfig} pluginConfig - the plugin configuration object 8 | * @param {Function} callback - callback upon completion of the critical CSS generation 9 | * @constructor 10 | */ 11 | declare function PluginCritical(pluginConfig: CriticalPluginConfig, callback?: CriticalPluginCallback): Plugin; 12 | 13 | export { PluginCritical as default }; 14 | -------------------------------------------------------------------------------- /src/__tests__/inline-true/__snapshots__/inline-true.test.ts.snap: -------------------------------------------------------------------------------- 1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html 2 | 3 | exports[`\`inline: true\` Critical CSS generation 1`] = ` 4 | " 5 | 6 | Critical CSS test 7 | 8 | 9 | 10 | 11 |

12 | hello, world. 13 |

14 | 15 | 16 | 17 | " 18 | `; 19 | -------------------------------------------------------------------------------- /dist/index.js: -------------------------------------------------------------------------------- 1 | import*as n from"path";var m="_critical.min.css",h={inline:!1,extract:!1,width:1200,height:1200,penthouse:{blockJSRequests:!1}};function p(i,r){return{name:"critical",async writeBundle(o,f){let c=[];for(let t of Object.values(f))if(t.type==="asset"&&t.fileName.endsWith(".css")){let a=n.join(o.dir||"",t.fileName);c.push(a)}if(c.length)for(let t of i.criticalPages){let a=i.criticalBase,s=i.criticalUrl+t.uri,l=i.criticalConfig&&i.criticalConfig.inline==!0?t.template+".html":t.template+m,g=Object.assign({css:c},h,{base:a,src:s,target:l},i.criticalConfig),u=(await import("critical")).generate;console.log(`Generating critical CSS from ${s} to ${l}`),await u(g,e=>{e&&console.error(e),r&&r(e)})}}}}var C=p;export{C as default}; 2 | //# sourceMappingURL=index.js.map -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "allowSyntheticDefaultImports": true, 5 | "baseUrl": "node_modules", 6 | "declaration": true, 7 | "esModuleInterop": true, 8 | "experimentalDecorators": true, 9 | "module": "esnext", 10 | "moduleResolution": "node", 11 | "noEmit": true, 12 | "noImplicitAny": true, 13 | "outDir": "./dist/", 14 | "paths": { 15 | "@/*": [ 16 | "./src/*" 17 | ] 18 | }, 19 | "resolveJsonModule": true, 20 | "skipLibCheck": true, 21 | "sourceMap": true, 22 | "strict": true, 23 | "strictBindCallApply": true, 24 | "strictFunctionTypes": true, 25 | "strictNullChecks": true, 26 | "target": "esnext", 27 | "types": [ 28 | "node", 29 | "vite/client" 30 | ] 31 | }, 32 | "include": [ 33 | "./src/**/*.ts", 34 | "./src/**/*.vue" 35 | ], 36 | "exclude": [ 37 | "./dist/**" 38 | ] 39 | } 40 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 nystudio107 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | import typescriptEslint from "@typescript-eslint/eslint-plugin"; 2 | import globals from "globals"; 3 | import path from "node:path"; 4 | import { fileURLToPath } from "node:url"; 5 | import js from "@eslint/js"; 6 | import { FlatCompat } from "@eslint/eslintrc"; 7 | 8 | const __filename = fileURLToPath(import.meta.url); 9 | const __dirname = path.dirname(__filename); 10 | const compat = new FlatCompat({ 11 | baseDirectory: __dirname, 12 | recommendedConfig: js.configs.recommended, 13 | allConfig: js.configs.all 14 | }); 15 | 16 | export default [ 17 | ...compat.extends("eslint:recommended", "plugin:@typescript-eslint/recommended"), 18 | { 19 | plugins: { 20 | "@typescript-eslint": typescriptEslint, 21 | }, 22 | 23 | languageOptions: { 24 | globals: { 25 | ...globals.browser, 26 | ...globals.amd, 27 | ...globals.node, 28 | }, 29 | 30 | ecmaVersion: 2020, 31 | sourceType: "module", 32 | 33 | parserOptions: { 34 | parser: "@typescript-eslint/parser", 35 | }, 36 | }, 37 | 38 | rules: { 39 | "no-undef": "off", 40 | "@typescript-eslint/ban-ts-comment": "off", 41 | }, 42 | }, 43 | ]; -------------------------------------------------------------------------------- /dist/index.cjs: -------------------------------------------------------------------------------- 1 | "use strict";var C=Object.create;var s=Object.defineProperty;var P=Object.getOwnPropertyDescriptor;var d=Object.getOwnPropertyNames;var S=Object.getPrototypeOf,b=Object.prototype.hasOwnProperty;var w=(t,i)=>{for(var c in i)s(t,c,{get:i[c],enumerable:!0})},g=(t,i,c,r)=>{if(i&&typeof i=="object"||typeof i=="function")for(let a of d(i))!b.call(t,a)&&a!==c&&s(t,a,{get:()=>i[a],enumerable:!(r=P(i,a))||r.enumerable});return t};var u=(t,i,c)=>(c=t!=null?C(S(t)):{},g(i||!t||!t.__esModule?s(c,"default",{value:t,enumerable:!0}):c,t)),j=t=>g(s({},"__esModule",{value:!0}),t);var N={};w(N,{default:()=>k});module.exports=j(N);var m=u(require("path"),1),x="_critical.min.css",y={inline:!1,extract:!1,width:1200,height:1200,penthouse:{blockJSRequests:!1}};function B(t,i){return{name:"critical",async writeBundle(c,r){let a=[];for(let e of Object.values(r))if(e.type==="asset"&&e.fileName.endsWith(".css")){let l=m.join(c.dir||"",e.fileName);a.push(l)}if(a.length)for(let e of t.criticalPages){let l=t.criticalBase,o=t.criticalUrl+e.uri,f=t.criticalConfig&&t.criticalConfig.inline==!0?e.template+".html":e.template+x,h=Object.assign({css:a},y,{base:l,src:o,target:f},t.criticalConfig),p=(await import("critical")).generate;console.log(`Generating critical CSS from ${o} to ${f}`),await p(h,n=>{n&&console.error(n),i&&i(n)})}}}}var k=B; 2 | //# sourceMappingURL=index.cjs.map -------------------------------------------------------------------------------- /src/__tests__/inline-false/inline-false.test.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import path from 'path'; 3 | import PluginCritical from '../../index'; 4 | import {Plugin} from 'rollup'; 5 | import {expect, test} from 'vitest' 6 | 7 | const testRoot = path.join(__dirname, '/'); 8 | const testOutputPath = path.join(testRoot, '__output__/test_critical.min.css'); 9 | 10 | const pluginConfig: CriticalPluginConfig = { 11 | criticalBase: testRoot, 12 | criticalUrl: testRoot, 13 | criticalPages: [ 14 | { 15 | uri: 'index.html', 16 | template: '__output__/test', 17 | } 18 | ], 19 | criticalConfig: { 20 | inline: false, 21 | }, 22 | }; 23 | 24 | test('`inline: false` Critical CSS generation', async () => { 25 | // Instantiate the Rollup plugin 26 | const plugin: Plugin = PluginCritical(pluginConfig); 27 | // Call the plugin to generate critical css 28 | if (plugin && typeof plugin.writeBundle === 'function') { 29 | // @ts-ignore 30 | await plugin.writeBundle({ 31 | dir: testRoot, 32 | }, { 33 | chunk: { 34 | type: 'asset', 35 | fileName: 'style.css', 36 | } 37 | }); 38 | // Compare the output with the snapshot 39 | expect(fs.readFileSync(testOutputPath).toString()) 40 | .toMatchSnapshot(); 41 | } 42 | }); 43 | -------------------------------------------------------------------------------- /src/__tests__/inline-true/inline-true.test.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import path from 'path'; 3 | import PluginCritical from '../../index'; 4 | import { Plugin } from 'rollup'; 5 | import {expect, test} from 'vitest' 6 | 7 | const testRoot = path.join(__dirname, '/'); 8 | const testOutputPath = path.join(testRoot, '__output__/test_index.html'); 9 | 10 | const pluginConfig: CriticalPluginConfig = { 11 | criticalBase: testRoot, 12 | criticalUrl: testRoot, 13 | criticalPages: [ 14 | { 15 | uri: 'index.html', 16 | template: '__output__/test_index', 17 | } 18 | ], 19 | criticalConfig: { 20 | inline: true, 21 | }, 22 | }; 23 | 24 | test('`inline: true` Critical CSS generation', async () => { 25 | // Instantiate the Rollup plugin 26 | const plugin: Plugin = PluginCritical(pluginConfig); 27 | // Call the plugin to generate critical css 28 | if (plugin && typeof plugin.writeBundle === 'function') { 29 | // @ts-ignore 30 | await plugin.writeBundle({ 31 | dir: testRoot, 32 | }, { 33 | chunk: { 34 | type: 'asset', 35 | fileName: 'style.css', 36 | } 37 | }); 38 | // Compare the output with the snapshot 39 | expect(fs.readFileSync(testOutputPath).toString()) 40 | .toMatchSnapshot(); 41 | } 42 | }); 43 | -------------------------------------------------------------------------------- /src/@types/rollup-plugin-critical.d.ts: -------------------------------------------------------------------------------- 1 | type CriticalPluginCallback = (err: string) => void; 2 | 3 | interface CriticalPages { 4 | /** Combined with `criticalUrl` to determine the URLs to scrape for Critical CSS */ 5 | uri: string; 6 | /** Critical CSS files are named with the `template` path, and saved to the `criticalBase` directory */ 7 | template: string; 8 | } 9 | 10 | interface CriticalPluginConfig { 11 | /** 12 | * The base URL to use in combination with the `criticalPages` `uri`s to determine the URLs to scrape for Critical CSS. 13 | * This can also be a file system path. This is combined with `criticalPages.uri` 14 | * to determine pages to scrap for critical CSS. 15 | * Determines the `criticalConfig.src` property 16 | */ 17 | criticalUrl: string; 18 | /** 19 | * The base file system path to where the generated Critical CSS file should be saved. 20 | * This is combined with `criticalPages.template` with `_critical.min.css` appended 21 | * to it to determine the saved critical CSS file name. 22 | * Determines the `criticalConfig.target` property 23 | */ 24 | criticalBase?: string; 25 | /** 26 | * An array objects that contain the page `uri`s that are combined with the `criticalUrl` to 27 | * determine the URLs to scrape for Critical CSS. The resulting files are named with the 28 | * `template` path, and saved to the `criticalBase` directory 29 | */ 30 | criticalPages: Partial[]; 31 | /** 32 | * This is the full [config for critical](https://github.com/addyosmani/critical#options) that is passed 33 | * through to the `critical` package. 34 | * You may optionally override any properties you like here 35 | */ 36 | criticalConfig?: Partial; 37 | } 38 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rollup-plugin-critical", 3 | "type": "module", 4 | "version": "1.0.15", 5 | "description": "Rollup plugin to generate critical CSS.", 6 | "author": "nystudio107", 7 | "license": "MIT", 8 | "keywords": [ 9 | "rollup", 10 | "plugin", 11 | "critical", 12 | "css" 13 | ], 14 | "homepage": "https://github.com/nystudio107/rollup-plugin-critical", 15 | "repository": { 16 | "type": "git", 17 | "url": "git+ssh://github.com/nystudio107/rollup-plugin-critical" 18 | }, 19 | "bugs": { 20 | "url": "https://github.com/nystudio107/rollup-plugin-critical/issues" 21 | }, 22 | "exports": { 23 | "types": "./dist/index.d.ts", 24 | "import": "./dist/index.js", 25 | "require": "./dist/index.cjs" 26 | }, 27 | "main": "dist/index.cjs", 28 | "module": "dist/index.js", 29 | "types": "dist/index.d.ts", 30 | "files": [ 31 | "dist", 32 | "LICENSE", 33 | "README.md" 34 | ], 35 | "scripts": { 36 | "build": "npm run lint && tsup src/index.ts --clean --minify --sourcemap --dts --format cjs,esm", 37 | "check": "tsc --noEmit", 38 | "dev": "nr build --watch", 39 | "lint": "tsc --noEmit && eslint './src/**/*.{js,ts,vue}' --fix", 40 | "prepublishOnly": "nr build", 41 | "release": "npm run lint && npm login && npm publish", 42 | "test": "vitest run --coverage", 43 | "test-coverage": "vitest run --coverage", 44 | "test-ci": "vitest run --coverage.enabled --coverage.reporter='text-summary'", 45 | "test-dev": "vitest" 46 | }, 47 | "dependencies": { 48 | "critical": "^7.0.0" 49 | }, 50 | "devDependencies": { 51 | "@antfu/ni": "^0.23.0", 52 | "@types/node": "^20.0.0", 53 | "@typescript-eslint/eslint-plugin": "^8.0.0", 54 | "@vitest/coverage-v8": "^2.0.0", 55 | "eslint": "^9.0.0", 56 | "rollup": "^4.0.0", 57 | "tsup": "^8.0.0", 58 | "typescript": "latest", 59 | "vite": "^5.0.0", 60 | "vitest": "^2.0.0" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | TAG?=22-alpine 2 | CONTAINER?=$(shell basename $(CURDIR)) 3 | DOCKER_RUN=docker container run --rm -it -v `pwd`:/app 4 | IMAGE_INFO=$(shell docker image inspect $(CONTAINER):$(TAG)) 5 | IMAGE_NAME=${CONTAINER}:${TAG} 6 | 7 | .PHONY: build clean dev image-build image-check lint release ssh test test-coverage test-dev npm 8 | 9 | # Perform a dist build 10 | build: image-check 11 | ${DOCKER_RUN} --name ${CONTAINER}-$@ ${IMAGE_NAME} run build 12 | # Remove node_modules/ & package-lock.json 13 | clean: 14 | rm -rf node_modules/ 15 | rm -f package-lock.json 16 | # Run in watch mode for development 17 | dev: image-check 18 | ${DOCKER_RUN} --name ${CONTAINER}-$@ ${IMAGE_NAME} run dev 19 | # Build the Docker image & run npm install 20 | image-build: 21 | docker build . -t ${IMAGE_NAME} --build-arg TAG=${TAG} --no-cache 22 | ${DOCKER_RUN} --name ${CONTAINER}-$@ ${IMAGE_NAME} install 23 | # Ensure the image has been created 24 | image-check: 25 | ifeq ($(IMAGE_INFO), []) 26 | image-check: image-build 27 | endif 28 | # Run eslint & tsc to check the code 29 | lint: image-check 30 | ${DOCKER_RUN} --name ${CONTAINER}-$@ ${IMAGE_NAME} run lint 31 | # Release a new version 32 | release: image-check 33 | ${DOCKER_RUN} --name ${CONTAINER}-$@ ${IMAGE_NAME} run release 34 | # ssh into the already running container 35 | ssh: image-check 36 | ${DOCKER_RUN} --name ${CONTAINER}-$@ --entrypoint=/bin/sh ${IMAGE_NAME} 37 | # Run tests via npm run test 38 | test: image-check 39 | ${DOCKER_RUN} --name ${CONTAINER}-$@ ${IMAGE_NAME} run test 40 | # Run tests with coverage via npm run test-coverage 41 | test-coverage: image-check 42 | ${DOCKER_RUN} --name ${CONTAINER}-$@ ${IMAGE_NAME} run test-coverage 43 | # Run tests in dev mode via npm run test-dev 44 | test-dev: image-check 45 | ${DOCKER_RUN} --name ${CONTAINER}-$@ ${IMAGE_NAME} run test-dev 46 | npm: docker 47 | ${DOCKER_RUN} --name ${CONTAINER}-$@ ${IMAGE_NAME} $(filter-out $@,$(MAKECMDGOALS)) $(MAKEFLAGS) 48 | %: 49 | @: 50 | # ref: https://stackoverflow.com/questions/6273608/how-to-pass-argument-to-makefile-from-command-line 51 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Rollup Plugin Critical 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | ## 1.0.15 - 2025.02.02 6 | ### Changed 7 | * Updated to Node 22 & npm 11 8 | * Updated to latest deps 9 | 10 | ## 1.0.14 - 2024.09.04 11 | ### Changed 12 | * Updated to the latest Critical & other deps 13 | 14 | ## 1.0.13 - 2023.11.22 15 | ### Changed 16 | * Switch over to using Node 20 and NPM 10 in the Dockerfile 17 | * Update to Vite `^5.0.0` and Vitest `^1.0.0-beta.5` 18 | 19 | ### Fix 20 | * Fix an issue where building with a project of `"type": "module"` would fail because conditional exports were not defined in `"exports"` ([#12](https://github.com/nystudio107/rollup-plugin-critical/issues/12)) 21 | 22 | ## 1.0.12 - 2022.12.12 23 | ### Fix 24 | * Fix import of the now ESM-only `critical` package into the CJS build of `rollup-plugin-critical` ([#9](https://github.com/nystudio107/rollup-plugin-critical/issues/9)) 25 | 26 | ## 1.0.11 - 2022.12.12 27 | ### Changed 28 | * Refactored the tests to use snapshots 29 | 30 | ### Fixed 31 | * Fixed an issue where the plugin was being bundled as ESM, when it should be CommonJS for broader (and backwards) compatibility ([#9](https://github.com/nystudio107/rollup-plugin-critical/issues/9)) 32 | 33 | ## 1.0.10 - 2022.12.11 34 | ### Added 35 | * Add `eslint` to the build phase 36 | 37 | ### Changed 38 | * Install the latest `npm` in the Docker image 39 | * Add `dev`, `release` commands, remove `update` dependency 40 | * Minify the `dist/` output 41 | * Clean up type definitions 42 | * Refactor to use `critical` `^5.0.0` 43 | * Switch tests from Jest to Vitest 44 | 45 | ## 1.0.9 - 2022.09.13 46 | ### Added 47 | * Added support for `inline: true` via ([#5](https://github.com/nystudio107/rollup-plugin-critical/pull/5)) 48 | 49 | ## 1.0.8 - 2021.10.15 50 | ### Changed 51 | * Switched to `critical` `^4.0.0` ([#2](https://github.com/nystudio107/rollup-plugin-critical/issues/2)) 52 | * Remove the `minify` option, since it was removed from `critical` `^4.0.0` 53 | 54 | ## 1.0.7 - 2021-06-02 55 | ### Changed 56 | * Switched to `tsup` for bundling 57 | 58 | ## 1.0.6 - 2021-06-01 59 | ### Fixed 60 | * Fixed build of `dist/index.d.ts` to have the correct default export by sourcing `index.ts` 61 | 62 | ## 1.0.5 - 2021-06-01 63 | ### Fixed 64 | * Fixed `respectExternal` setting 65 | 66 | ## 1.0.4 - 2021-06-01 67 | ### Changed 68 | * Split types out into separate files 69 | * Use Rollup and dts to build the types 70 | 71 | ## 1.0.3 - 2021-05-31 72 | ### Fixed 73 | * Fixed `dist/index.d.ts` to have complete type definitions 74 | 75 | ## 1.0.2 - 2021-05-31 76 | ### Changed 77 | * Administrivia 78 | 79 | ## 1.0.1 - 2021-05-31 80 | ### Changed 81 | * Remove unneeded conditionals 82 | * Improve comment typehints in jsdoc 83 | * Remove vestigial `concurrency` property 84 | 85 | ## 1.0.0 - 2021-05-30 86 | ### Added 87 | * Initial release 88 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import {Plugin} from 'rollup'; 2 | import * as path from 'path'; 3 | 4 | const criticalSuffix = '_critical.min.css'; 5 | 6 | /** 7 | * Default `criticalConfig` passed in to `critical` 8 | */ 9 | const defaultCriticalConfig: Partial = { 10 | inline: false, 11 | extract: false, 12 | width: 1200, 13 | height: 1200, 14 | penthouse: { 15 | blockJSRequests: false 16 | } 17 | }; 18 | 19 | /** 20 | * [Vite.js](https://vitejs.dev/) & [Rollup](https://rollupjs.org/) plugin for generating critical CSS 21 | * that uses the [critical](https://github.com/addyosmani/critical) generator under the hood. 22 | * 23 | * @param {CriticalPluginConfig} pluginConfig - the plugin configuration object 24 | * @param {Function} callback - callback upon completion of the critical CSS generation 25 | * @constructor 26 | */ 27 | function PluginCritical(pluginConfig: CriticalPluginConfig, callback?: CriticalPluginCallback): Plugin { 28 | return { 29 | name: 'critical', 30 | async writeBundle(outputOptions, bundle) { 31 | const css: Array = []; 32 | // Find all of the generated CSS assets 33 | for (const chunk of Object.values(bundle)) { 34 | if (chunk.type === 'asset' && chunk.fileName.endsWith('.css')) { 35 | const cssFile = path.join(outputOptions.dir || '', chunk.fileName); 36 | css.push(cssFile); 37 | } 38 | } 39 | // If we have no CSS, skip bundle 40 | if (!css.length) { 41 | return; 42 | } 43 | // Iterate through the pages 44 | for (const page of pluginConfig.criticalPages) { 45 | const criticalBase = pluginConfig.criticalBase; 46 | const criticalSrc = pluginConfig.criticalUrl + page.uri; 47 | // If inline is set to true, use HTML as target, otherwise CSS with suffix 48 | const criticalTarget = (pluginConfig.criticalConfig && pluginConfig.criticalConfig.inline == true) ? page.template + ".html" : page.template + criticalSuffix; 49 | // Merge in our options 50 | const options = Object.assign( 51 | { css }, 52 | defaultCriticalConfig, 53 | { 54 | base: criticalBase, 55 | src: criticalSrc, 56 | target: criticalTarget, 57 | }, 58 | pluginConfig.criticalConfig 59 | ); 60 | // Horrible nonsense to import an ESM module into CJS 61 | // ref: https://adamcoster.com/blog/commonjs-and-esm-importexport-compatibility-examples 62 | const generate = (await import('critical')).generate; 63 | // Generate the Critical CSS 64 | console.log(`Generating critical CSS from ${criticalSrc} to ${criticalTarget}`); 65 | await generate(options, (err: string) => { 66 | if (err) { 67 | console.error(err); 68 | } 69 | if (callback) { 70 | callback(err); 71 | } 72 | }); 73 | } 74 | } 75 | } 76 | } 77 | 78 | export default PluginCritical; 79 | -------------------------------------------------------------------------------- /src/@types/penthouse.d.ts: -------------------------------------------------------------------------------- 1 | type PenthouseAllowedResponseCallback = (response: object) => boolean; 2 | 3 | interface PenthouseConfig { 4 | /** Accessible url. Use file:/// protocol for local html files. */ 5 | url: string; 6 | /** Original css to extract critical css from */ 7 | cssString: string; 8 | /** Path to original css file on disk (if using instead of `cssString`) */ 9 | css: string; 10 | /** Width for critical viewport */ 11 | width: number; 12 | /** Height for critical viewport */ 13 | height: number; 14 | /** Configuration for screenshots (not used by default). See [Screenshot example](https://github.com/pocketjoso/penthouse/blob/master/examples/screenshots.js) */ 15 | screenshots: object; 16 | /** Keep media queries even for width/height values larger than critical viewport. */ 17 | keepLargerMediaQueries: boolean; 18 | /** 19 | * Array of css selectors to keep in critical css, even if not appearing in critical viewport. 20 | * Strings or regex (f.e. `['.keepMeEvenIfNotSeenInDom', /^\.button/]`) 21 | */ 22 | forceInclude: Array; 23 | /** 24 | * Array of css selectors to remove in critical css, even if appearing in critical viewport. 25 | * Strings or regex (f.e. `['.doNotKeepMeEvenIfNotSeenInDom', /^\.button/]`) 26 | */ 27 | forceExclude: Array; 28 | /** Css properties to filter out from critical css */ 29 | propertiesToRemove: Array; 30 | /** Ms; abort critical CSS generation after this time */ 31 | timeout: number; 32 | /** Settings for puppeteer. See [Custom puppeteer browser example](https://github.com/pocketjoso/penthouse/blob/master/examples/custom-browser.js) */ 33 | puppeteer: object; 34 | /** Ms; stop waiting for page load after this time (for sites when page load event is unreliable) */ 35 | pageLoadSkipTimeout: number; 36 | /** 37 | * ms; wait time after page load before critical css extraction starts 38 | * (also before "before" screenshot is taken, if used) 39 | */ 40 | renderWaitTime: number; 41 | /** set to false to load JS (not recommended) */ 42 | blockJSRequests: boolean; 43 | /** characters; strip out inline base64 encoded resources larger than this */ 44 | maxEmbeddedBase64Length: number; 45 | /** Can be specified to limit nr of elements to inspect per css selector, reducing execution time. */ 46 | maxElementsToCheckPerSelector: number; 47 | /** specify which user agent string when loading the page */ 48 | userAgent: string; 49 | /** Set extra http headers to be sent with the request for the url. */ 50 | customPageHeaders: object; 51 | /** For formatting of each cookie, see [Puppeteer setCookie docs](https://github.com/puppeteer/puppeteer/blob/v1.9.0/docs/api.md#pagesetcookiecookies) */ 52 | cookies: Array; 53 | /** Make Penthouse throw on errors parsing the original CSS. Legacy option, not recommended */ 54 | strict: boolean; 55 | /** 56 | * Let Penthouse stop if the server response code is not matching this value. number and 57 | * regex types are tested against the [response.status()](https://github.com/puppeteer/puppeteer/blob/v1.14.0/docs/api.md#responsestatus). A function is also allowed and 58 | * gets [Response](https://github.com/puppeteer/puppeteer/blob/v1.14.0/docs/api.md#class-response) as argument. The function should return a boolean. 59 | */ 60 | allowedResponseCode: number | RegExp | PenthouseAllowedResponseCallback; 61 | } 62 | -------------------------------------------------------------------------------- /src/@types/critical.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'critical'; 2 | 3 | type DeclCallback = (node: object, value: string) => boolean; 4 | 5 | interface PostcssUrlAsset { 6 | /** original url */ 7 | url: string; 8 | /** url pathname (url without search or hash) */ 9 | pathname: string; 10 | /** absolute path to asset */ 11 | absolutePath: string; 12 | /** current relative path to asset */ 13 | relativePath: string; 14 | /** search from url, ex. ?query=1 from ./image.png?query=1 **/ 15 | search: string; 16 | /** hash from url, ex. #spriteLink from ../asset.svg#spriteLink */ 17 | hash: string; 18 | } 19 | 20 | type RebaseFn = (asset: PostcssUrlAsset) => string; 21 | 22 | interface RebaseConfig { 23 | from: string; 24 | to: string; 25 | } 26 | 27 | interface CriticalConfig { 28 | /** Inline critical-path CSS using Filament Group's loadCSS. Pass an object to configure `inline-critical` */ 29 | inline: boolean; 30 | /** Base directory in which the source and destination are to be written */ 31 | base: string; 32 | /** HTML source to be operated against. This option takes precedence over the `src` option */ 33 | html: string; 34 | /** An array of paths to css files, file globs or Vinyl file objects. */ 35 | css: Array; 36 | /** Location of the HTML source to be operated against */ 37 | src: string; 38 | /** 39 | * Location of where to save the output of an operation. 40 | * Use an object with 'html' and 'css' props if you want to store both 41 | */ 42 | target: string | Partial<{ 43 | css: string; 44 | html: string; 45 | uncritical: string; 46 | }>; 47 | /** Width of the target viewport */ 48 | width: number; 49 | /** Height of the target viewport */ 50 | height: number; 51 | /** 52 | * Remove the inlined styles from any stylesheets referenced in the HTML. 53 | * It generates new references based on extracted content so it's safe to use for 54 | * multiple HTML files referencing the same stylesheet. Use with caution. 55 | * Removing the critical CSS per page results in a unique async loaded CSS file for every page. 56 | * Meaning you can't rely on cache across multiple pages 57 | */ 58 | extract: boolean; 59 | /** Inline images */ 60 | inlineImages: boolean; 61 | /** List of directories/urls where the inliner should start looking for assets */ 62 | assetPaths: Array; 63 | /** Sets a max file size (in bytes) for base64 inlined images */ 64 | maxImageFileSize: number; 65 | /** 66 | * Critical tries it's best to rebase the asset paths relative to the document. 67 | * If this doesn't work as expected you can always use this option to control the rebase paths. 68 | * See postcss-url for details. (https://github.com/pocketjoso/penthouse#usage-1). 69 | */ 70 | rebase: RebaseConfig | RebaseFn; 71 | /** ignore CSS rules */ 72 | ignore: Partial<{ 73 | atrule: Array; 74 | rule: Array; 75 | decl: DeclCallback; 76 | }>; 77 | /** User agent to use when fetching a remote src */ 78 | userAgent: string; 79 | /** Configuration options for `penthouse`. */ 80 | penthouse: Partial; 81 | /** Configuration options for `got`. */ 82 | request: object; 83 | /** RFC2617 basic authorization: `user` */ 84 | user: string; 85 | /** RFC2617 basic authorization: `pass` */ 86 | pass: string; 87 | /** Throw an error if no css is found */ 88 | strict: boolean; 89 | } 90 | -------------------------------------------------------------------------------- /dist/index.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import {Plugin} from 'rollup';\nimport * as path from 'path';\n\nconst criticalSuffix = '_critical.min.css';\n\n/**\n * Default `criticalConfig` passed in to `critical`\n */\nconst defaultCriticalConfig: Partial = {\n inline: false,\n extract: false,\n width: 1200,\n height: 1200,\n penthouse: {\n blockJSRequests: false\n }\n};\n\n/**\n * [Vite.js](https://vitejs.dev/) & [Rollup](https://rollupjs.org/) plugin for generating critical CSS\n * that uses the [critical](https://github.com/addyosmani/critical) generator under the hood.\n *\n * @param {CriticalPluginConfig} pluginConfig - the plugin configuration object\n * @param {Function} callback - callback upon completion of the critical CSS generation\n * @constructor\n */\nfunction PluginCritical(pluginConfig: CriticalPluginConfig, callback?: CriticalPluginCallback): Plugin {\n return {\n name: 'critical',\n async writeBundle(outputOptions, bundle) {\n const css: Array = [];\n // Find all of the generated CSS assets\n for (const chunk of Object.values(bundle)) {\n if (chunk.type === 'asset' && chunk.fileName.endsWith('.css')) {\n const cssFile = path.join(outputOptions.dir || '', chunk.fileName);\n css.push(cssFile);\n }\n }\n // If we have no CSS, skip bundle\n if (!css.length) {\n return;\n }\n // Iterate through the pages\n for (const page of pluginConfig.criticalPages) {\n const criticalBase = pluginConfig.criticalBase;\n const criticalSrc = pluginConfig.criticalUrl + page.uri;\n // If inline is set to true, use HTML as target, otherwise CSS with suffix\n const criticalTarget = (pluginConfig.criticalConfig && pluginConfig.criticalConfig.inline == true) ? page.template + \".html\" : page.template + criticalSuffix;\n // Merge in our options\n const options = Object.assign(\n { css },\n defaultCriticalConfig,\n {\n base: criticalBase,\n src: criticalSrc,\n target: criticalTarget,\n },\n pluginConfig.criticalConfig\n );\n // Horrible nonsense to import an ESM module into CJS\n // ref: https://adamcoster.com/blog/commonjs-and-esm-importexport-compatibility-examples\n const generate = (await import('critical')).generate;\n // Generate the Critical CSS\n console.log(`Generating critical CSS from ${criticalSrc} to ${criticalTarget}`);\n await generate(options, (err: string) => {\n if (err) {\n console.error(err);\n }\n if (callback) {\n callback(err);\n }\n });\n }\n }\n }\n}\n\nexport default PluginCritical;\n"],"mappings":"AACA,UAAYA,MAAU,OAEtB,IAAMC,EAAiB,oBAKjBC,EAAiD,CACrD,OAAQ,GACR,QAAS,GACT,MAAO,KACP,OAAQ,KACR,UAAW,CACT,gBAAiB,EACnB,CACF,EAUA,SAASC,EAAeC,EAAoCC,EAA2C,CACrG,MAAO,CACL,KAAM,WACN,MAAM,YAAYC,EAAeC,EAAQ,CACvC,IAAMC,EAAqB,CAAC,EAE5B,QAAWC,KAAS,OAAO,OAAOF,CAAM,EACtC,GAAIE,EAAM,OAAS,SAAWA,EAAM,SAAS,SAAS,MAAM,EAAG,CAC7D,IAAMC,EAAe,OAAKJ,EAAc,KAAO,GAAIG,EAAM,QAAQ,EACjED,EAAI,KAAKE,CAAO,CAClB,CAGF,GAAKF,EAAI,OAIT,QAAWG,KAAQP,EAAa,cAAe,CAC7C,IAAMQ,EAAeR,EAAa,aAC5BS,EAAcT,EAAa,YAAcO,EAAK,IAE9CG,EAAkBV,EAAa,gBAAkBA,EAAa,eAAe,QAAU,GAAQO,EAAK,SAAW,QAAUA,EAAK,SAAWV,EAEzIc,EAAU,OAAO,OACnB,CAAE,IAAAP,CAAI,EACNN,EACA,CACE,KAAMU,EACN,IAAKC,EACL,OAAQC,CACV,EACAV,EAAa,cACjB,EAGMY,GAAY,KAAM,QAAO,UAAU,GAAG,SAE5C,QAAQ,IAAI,gCAAgCH,CAAW,OAAOC,CAAc,EAAE,EAC9E,MAAME,EAASD,EAAUE,GAAgB,CACnCA,GACF,QAAQ,MAAMA,CAAG,EAEfZ,GACFA,EAASY,CAAG,CAEhB,CAAC,CACH,CACF,CACF,CACF,CAEA,IAAOC,EAAQf","names":["path","criticalSuffix","defaultCriticalConfig","PluginCritical","pluginConfig","callback","outputOptions","bundle","css","chunk","cssFile","page","criticalBase","criticalSrc","criticalTarget","options","generate","err","index_default"]} -------------------------------------------------------------------------------- /dist/index.cjs.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import {Plugin} from 'rollup';\nimport * as path from 'path';\n\nconst criticalSuffix = '_critical.min.css';\n\n/**\n * Default `criticalConfig` passed in to `critical`\n */\nconst defaultCriticalConfig: Partial = {\n inline: false,\n extract: false,\n width: 1200,\n height: 1200,\n penthouse: {\n blockJSRequests: false\n }\n};\n\n/**\n * [Vite.js](https://vitejs.dev/) & [Rollup](https://rollupjs.org/) plugin for generating critical CSS\n * that uses the [critical](https://github.com/addyosmani/critical) generator under the hood.\n *\n * @param {CriticalPluginConfig} pluginConfig - the plugin configuration object\n * @param {Function} callback - callback upon completion of the critical CSS generation\n * @constructor\n */\nfunction PluginCritical(pluginConfig: CriticalPluginConfig, callback?: CriticalPluginCallback): Plugin {\n return {\n name: 'critical',\n async writeBundle(outputOptions, bundle) {\n const css: Array = [];\n // Find all of the generated CSS assets\n for (const chunk of Object.values(bundle)) {\n if (chunk.type === 'asset' && chunk.fileName.endsWith('.css')) {\n const cssFile = path.join(outputOptions.dir || '', chunk.fileName);\n css.push(cssFile);\n }\n }\n // If we have no CSS, skip bundle\n if (!css.length) {\n return;\n }\n // Iterate through the pages\n for (const page of pluginConfig.criticalPages) {\n const criticalBase = pluginConfig.criticalBase;\n const criticalSrc = pluginConfig.criticalUrl + page.uri;\n // If inline is set to true, use HTML as target, otherwise CSS with suffix\n const criticalTarget = (pluginConfig.criticalConfig && pluginConfig.criticalConfig.inline == true) ? page.template + \".html\" : page.template + criticalSuffix;\n // Merge in our options\n const options = Object.assign(\n { css },\n defaultCriticalConfig,\n {\n base: criticalBase,\n src: criticalSrc,\n target: criticalTarget,\n },\n pluginConfig.criticalConfig\n );\n // Horrible nonsense to import an ESM module into CJS\n // ref: https://adamcoster.com/blog/commonjs-and-esm-importexport-compatibility-examples\n const generate = (await import('critical')).generate;\n // Generate the Critical CSS\n console.log(`Generating critical CSS from ${criticalSrc} to ${criticalTarget}`);\n await generate(options, (err: string) => {\n if (err) {\n console.error(err);\n }\n if (callback) {\n callback(err);\n }\n });\n }\n }\n }\n}\n\nexport default PluginCritical;\n"],"mappings":"0jBAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,aAAAE,IAAA,eAAAC,EAAAH,GACA,IAAAI,EAAsB,qBAEhBC,EAAiB,oBAKjBC,EAAiD,CACrD,OAAQ,GACR,QAAS,GACT,MAAO,KACP,OAAQ,KACR,UAAW,CACT,gBAAiB,EACnB,CACF,EAUA,SAASC,EAAeC,EAAoCC,EAA2C,CACrG,MAAO,CACL,KAAM,WACN,MAAM,YAAYC,EAAeC,EAAQ,CACvC,IAAMC,EAAqB,CAAC,EAE5B,QAAWC,KAAS,OAAO,OAAOF,CAAM,EACtC,GAAIE,EAAM,OAAS,SAAWA,EAAM,SAAS,SAAS,MAAM,EAAG,CAC7D,IAAMC,EAAe,OAAKJ,EAAc,KAAO,GAAIG,EAAM,QAAQ,EACjED,EAAI,KAAKE,CAAO,CAClB,CAGF,GAAKF,EAAI,OAIT,QAAWG,KAAQP,EAAa,cAAe,CAC7C,IAAMQ,EAAeR,EAAa,aAC5BS,EAAcT,EAAa,YAAcO,EAAK,IAE9CG,EAAkBV,EAAa,gBAAkBA,EAAa,eAAe,QAAU,GAAQO,EAAK,SAAW,QAAUA,EAAK,SAAWV,EAEzIc,EAAU,OAAO,OACnB,CAAE,IAAAP,CAAI,EACNN,EACA,CACE,KAAMU,EACN,IAAKC,EACL,OAAQC,CACV,EACAV,EAAa,cACjB,EAGMY,GAAY,KAAM,QAAO,UAAU,GAAG,SAE5C,QAAQ,IAAI,gCAAgCH,CAAW,OAAOC,CAAc,EAAE,EAC9E,MAAME,EAASD,EAAUE,GAAgB,CACnCA,GACF,QAAQ,MAAMA,CAAG,EAEfZ,GACFA,EAASY,CAAG,CAEhB,CAAC,CACH,CACF,CACF,CACF,CAEA,IAAOnB,EAAQK","names":["index_exports","__export","index_default","__toCommonJS","path","criticalSuffix","defaultCriticalConfig","PluginCritical","pluginConfig","callback","outputOptions","bundle","css","chunk","cssFile","page","criticalBase","criticalSrc","criticalTarget","options","generate","err"]} -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Build](https://github.com/nystudio107/rollup-plugin-critical/actions/workflows/node.js.yml/badge.svg) 2 | ![Coverage Badge](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/khalwat/550f6ee414a26e0c8eae7cb6af3c214e/raw/rollup-plugin-critical__heads_master.json) 3 | [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/nystudio107/rollup-plugin-critical/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/nystudio107/rollup-plugin-critical/?branch=master) 4 | [![Code Coverage](https://scrutinizer-ci.com/g/nystudio107/rollup-plugin-critical/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/nystudio107/rollup-plugin-critical/?branch=master) 5 | [![Build Status](https://scrutinizer-ci.com/g/nystudio107/rollup-plugin-critical/badges/build.png?b=master)](https://scrutinizer-ci.com/g/nystudio107/rollup-plugin-critical/build-status/master) 6 | 7 | # rollup-plugin-critical 8 | 9 | [Vite.js](https://vitejs.dev/) & [Rollup](https://rollupjs.org/) plugin for generating critical CSS that uses the [critical](https://github.com/addyosmani/critical) generator under the hood. 10 | 11 | ## Install 12 | 13 | ```bash 14 | npm i -D rollup-plugin-critical 15 | ``` 16 | 17 | ## Usage 18 | 19 | ```js 20 | // rollup.config.js 21 | 22 | import PluginCritical from 'rollup-plugin-critical'; 23 | 24 | export default { 25 | input: 'index.js', 26 | output: { 27 | dir: 'dist', 28 | format: 'es', 29 | }, 30 | plugins: [ 31 | PluginCritical({ 32 | criticalUrl: 'https://nystudio107.com/', 33 | criticalBase: './', 34 | criticalPages: [ 35 | { uri: '', template: 'index' }, 36 | { uri: 'about', template: 'about/index' }, 37 | ], 38 | criticalConfig: { 39 | }, 40 | }), 41 | ], 42 | } 43 | ``` 44 | 45 | ## Options 46 | 47 | ### `criticalUrl: string` 48 | 49 | The base URL to use in combination with the `criticalPages` `uri`s to determine the URLs to scrape for Critical CSS. 50 | 51 | This can also be a file system path. This is combined with `criticalPages.uri` (see below) to determine pages to scrap for critical CSS. 52 | 53 | Determines the `criticalConfig.src` property (see below) 54 | 55 | ### `criticalBase: string` 56 | 57 | The base file system path to where the generated Critical CSS file should be saved. 58 | 59 | This is combined with `criticalPages.template` (see below) with `_critical.min.css` appended to it to determine the saved critical CSS file name. 60 | 61 | Determines the `criticalConfig.target` property (see below) 62 | 63 | ### `criticalPages: array of objects` 64 | 65 | An array objects that contain the page `uri`s that are combined with the `criticalUrl` to determine the URLs to scrape for Critical CSS. 66 | 67 | The resulting files are named with the `template` path, and saved to the `criticalBase` directory 68 | 69 | ### `criticalConfig: object` 70 | 71 | This is the full [config for critical](https://github.com/addyosmani/critical#options) that is passed through to the `critical` package. 72 | 73 | You may optionally override any properties you like here. The default values passed in are: 74 | 75 | ```ts 76 | const defaultCriticalConfig: Partial = { 77 | inline: false, 78 | extract: false, 79 | width: 1200, 80 | height: 1200, 81 | penthouse: { 82 | blockJSRequests: false 83 | } 84 | }; 85 | ``` 86 | 87 | The following [critical config properties](https://github.com/addyosmani/critical#options) are set dynamically by `rollup-plugin-critical`, but can be overridden via `criticalConfig`: 88 | 89 | - **`css`** - set to the css files that are generated in the Rollup build 90 | - **`base`** - property is set to `criticalBase` 91 | - **`src`** - derived from `criticalUrl` and `criticalPages.uri` 92 | - **`target`** - derived from `criticalPages.template` with `_critical.min.css` appended to it. If the `inline` option is set to `true`, the suffix `.html` is appended instead. 93 | ## License 94 | 95 | [MIT](LICENSE) © [nystudio107](https://nystudio107.com) 96 | --------------------------------------------------------------------------------