Do you want to request a feature or report a bug?
feature/question
What is the current behavior?
Contents from src/style/index.css is being loaded through link tag
If the current behavior is a bug, please provide the steps to reproduce.
What is the expected behavior?
Being able to inline contents from src/style/index.css by using a flag, or modifying preact.config.js or custom template.
If this is a feature request, what is motivation or use case for changing the behavior?
Inline critical css to prevent fetching it.
Please mention other relevant information.
You already can do that by modifying preact.config.js (e.g. by using style-ext-html-webpack-plugin).
Yup or by modifying the config to include my webpack-critical plugin
Thanks for the quick reply.
I'm having trouble getting webpack-critical to work, most probably I'm not setting it right. This is what I have in preact.config.js.
How would you modify the config to include webpack-critical?
Thanks again!
import { resolve } from 'path';
import WebpackCritical from 'webpack-critical';
export default function (config, env, helpers) {
if (env.production) {
config.output.publicPath = "https://some-cdn.net/";
}
const dist = resolve('build');
config.plugins.push(new WebpackCritical({
context: dist
}));
}
Hey @fedemar, turns out I had to update my plugin! A combination of webpack & html-webpack-plugin updates broke my hook listener.
If you update to [email protected], all you need to do is this:
// preact.config.js
const WebpackCritical = require('webpack-critical');
module.exports = function (config, env) {
if (env.production) {
config.plugins.push(
new WebpackCritical({
context: env.dest
})
)
}
}
And here's a live demo of the default template & webpack-critical running together~
Hi @lukeed , sorry to bug you again.
I just updated to [email protected] and modified preact.config.js like so.
const WebpackCritical = require('webpack-critical');
export default function (config, env, helpers) {
if (env.production) {
config.output.publicPath = "https://some-cdn.net/";
config.plugins.push(
new WebpackCritical({
context: env.dest
})
)
}
}
The error I got now is this
Html Webpack Plugin:
TypeError: Cannot read property 'source' of undefined
- index.js:19 Compilation.bundle.plugin
[app]/[webpack-critical]/index.js:19:39
- Tapable.js:272 Compilation.applyPluginsAsyncWaterfall
[v8.1.2]/[preact-cli]/[tapable]/lib/Tapable.js:272:13
- util.js:16 Compilation.tryCatcher
[v8.1.2]/[preact-cli]/[bluebird]/js/release/util.js:16:23
- index.js:645
[v8.1.2]/[preact-cli]/[html-webpack-plugin]/index.js:645:12
- index.js:165
[v8.1.2]/[preact-cli]/[html-webpack-plugin]/index.js:165:16
- util.js:16 tryCatcher
[v8.1.2]/[preact-cli]/[bluebird]/js/release/util.js:16:23
- promise.js:512 Promise._settlePromiseFromHandler
[v8.1.2]/[preact-cli]/[bluebird]/js/release/promise.js:512:31
- promise.js:569 Promise._settlePromise
[v8.1.2]/[preact-cli]/[bluebird]/js/release/promise.js:569:18
- promise.js:614 Promise._settlePromise0
[v8.1.2]/[preact-cli]/[bluebird]/js/release/promise.js:614:10
- promise.js:693 Promise._settlePromises
[v8.1.2]/[preact-cli]/[bluebird]/js/release/promise.js:693:18
- async.js:133 Async._drainQueue
[v8.1.2]/[preact-cli]/[bluebird]/js/release/async.js:133:16
- async.js:143 Async._drainQueues
[v8.1.2]/[preact-cli]/[bluebird]/js/release/async.js:143:10
- async.js:17 Immediate.Async.drainQueues
[v8.1.2]/[preact-cli]/[bluebird]/js/release/async.js:17:14
Ah, I know why.
It doesn't like the config.output.publicPath = "https://some-cdn.net/"; line.
If you can remove right now, it'll work. I have a fix incoming in ~3 minutes.
Fixed in [email protected] 😉
I see the css inlined in the head now! :raised_hands: but I also notice a link tag fetching the same css content.
Is there a way to prevent fetching the same content that's already inlined in the head?
For now it's actually intentional. You typically don't want to inline ALL your CSS as that's a bit counter productive to the performance purpose of inlining.
The only TODO on my list is extract the CSS from stylesheet so that there aren't any duplicate styles.
Hey @lukeed,
This might be a bit of an edge case but I'm trying to use this plugin when pre-rendering multiple routes that have different styling e.g. a home page vs a sign up page.
Is there a way to get the plugin to run per route vs all of them in one go?
Thanks!
Hey @matthewlynch, I don't think it's an edge case at all! Unfortunately, the plugin doesn't do it yet, but I'd like to get it to that point. Feel free to start a discussion in the plugin's repo -- don't wanna hijack here :P
You are probably already at this point, but you you _can_ prerender multiple routes, but they'd all contain the same inlined CSS... which is kind of unfortunate.
Hi @lukeed , let's say I only want to inline css for the landing page and don't have a link tag fetching the same css content. What would you recommend I do?
I'd probably look into the other plugin mentioned in this thread. I'm pretty sure it does what you're describing.
Mine isn't meant for everyone use case :)
Thanks @lukeed appreciate your help!
For anybody visiting this issue, I managed to find a work around to my problem.
I'm not sure if it's necessarily the best solution but it works!
const path = require('path');
const HtmlCriticalPlugin = require('html-critical-webpack-plugin');
const myPrerenderRoutesConfig = require('./src/prerender-urls');
export default (config, env, helpers) => {
if (!env.ssr && env.production) {
myPrerenderRoutesConfig
.map(route => {
const fileName = 'index.html';
if (route.url === '/') {
return `.${route.url}${fileName}`;
}
return `.${route.url}/${fileName}`;
})
.forEach(filePath => {
config.plugins.push(
new HtmlCriticalPlugin({
base: path.resolve(__dirname, 'build'),
src: filePath,
dest: filePath,
inline: true,
minify: true,
extract: false,
width: 1280,
height: 600
})
);
});
}
}
Might be worth publishing that as a preact-cli plugin @matthewlynch! I would certainly be interested in using it.
I’ll have a go at creating the preact-cli plugin this evening @developit 🙂.
As promised, here's a first pass at putting the above code into a plugin: https://github.com/matthewlynch/preact-cli-plugin-critical-css.
I've tested it in the project I'm currently building and it seems to be working as expected.
I'd love to hear any feedback you have so please let me know!
Looks good! Glad it's already added to the Wiki too 😄
Looks "solved" to me, but @fedemar please let us know if needs to be reopened.
Hi @lukeed,
I've set up preact using zero-to-preact guide and got around what I was looking for, having some styles being inlined in the head and some others using the link tag.
The webpack.config.js file looks like this
module: {
rules: [
...,
{
test: /inline.css$/,
use: ['style-loader',{ loader: 'css-loader', options: {minimize:true} }]
},
{
test: /link.css$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [{ loader: 'css-loader', options: {minimize:true} }]
})
}
]
},
plugins: [
...
new ExtractTextPlugin({filename: 'app.bundle.css'}),
...
]
The result is that every file ending with inline.css gets inlined in the head, and every file ending with link.css gets included through a link tag.
My question is, how can I replicate this behavior using preact.config.js?
@fedemar may be this can help https://github.com/developit/preact-cli/issues/464
Hi @prateekbh, thanks for the suggestion.
I tried with
config.module.loaders.push({
test: /inline.css$/,
loader: ['style-loader',{ loader: 'css-loader', options: {minimize:true} }]
});
but is throwing the following error when building
✖ ERROR ./src/style/index.inline.css
Module build failed: ModuleBuildError: Module build failed: Syntax Error
(5:1) Unknown word
3 | // load the styles
4 | var content = require("!!../../../../../nvm./index.inline.css");
> 5 | if(typeof content === 'string') content = [[module.id, content, '']];
| ^
6 | // Prepare cssTransformation
7 | var transform;
at runLoaders (C:\nvm\v8.1.2\node_modules\preact-cli\node_modules\webpack\lib\NormalModule.js:195:19)
at C:\nvm\v8.1.2\node_modules\preact-cli\node_modules\loader-runner\lib\LoaderRunner.js:364:11
at C:\nvm\v8.1.2\node_modules\preact-cli\node_modules\loader-runner\lib\LoaderRunner.js:230:18
at context.callback (C:\nvm\v8.1.2\node_modules\preact-cli\node_modules\loader-runner\lib\LoaderRunner.js:111:13)
at Promise.resolve.then.then.catch (C:\nvm\v8.1.2\node_modules\preact-cli\node_modules\postcss-loader\lib\index.js:189:44)
at tryCatcher (C:\nvm\v8.1.2\node_modules\preact-cli\node_modules\bluebird\js\release\util.js:16:23)
at Promise._settlePromiseFromHandler (C:\nvm\v8.1.2\node_modules\preact-cli\node_modules\bluebird\js\release\promise.js:512:31)
at Promise._settlePromise (C:\nvm\v8.1.2\node_modules\preact-cli\node_modules\bluebird\js\release\promise.js:569:18)
at Promise._settlePromise0 (C:\nvm\v8.1.2\node_modules\preact-cli\node_modules\bluebird\js\release\promise.js:614:10)
at Promise._settlePromises (C:\nvm\v8.1.2\node_modules\preact-cli\node_modules\bluebird\js\release\promise.js:689:18)
at Async._drainQueue (C:\nvm\v8.1.2\node_modules\preact-cli\node_modules\bluebird\js\release\async.js:133:16)
at Async._drainQueues (C:\nvm\v8.1.2\node_modules\preact-cli\node_modules\bluebird\js\release\async.js:143:10)
at Immediate.Async.drainQueues (C:\nvm\v8.1.2\node_modules\preact-cli\node_modules\bluebird\js\release\async.js:17:14)
at runCallback (timers.js:800:20)
at tryOnImmediate (timers.js:762:5)
at processImmediate [as _immediateCallback] (timers.js:733:5)
@ ./src/style/index.inline.css
@ ./src/index.js
@ C:/nvm/v8.1.2/node_modules/preact-cli/lib/lib/entry.js
Build failed!
Should I modify the existing css-loaders instead of adding new ones?
helpers.getLoadersByName(config, 'css-loader')
[
{
"rule": {
"test": /\.(css|less|s[ac]ss|styl)$/,
"include": [
"C:\\glamst\\gst-new-moodmachine-web\\app\\src\\components",
"C:\\glamst\\gst-new-moodmachine-web\\app\\src\\routes"
],
"loader": [
{
"loader": "C:\\nvm\\v8.1.2\\node_modules\\preact-cli\\node_modules\\extract-text-webpack-plugin\\dist\\loader.js",
"options": {
"omit": 1,
"remove": true
}
},
{
"loader": "style-loader"
},
{
"loader": "css-loader",
"options": {
"modules": true,
"localIdentName": "[local]__[hash:base64:5]",
"importLoaders": 1,
"sourceMap": true
}
},
{
"loader": "postcss-loader",
"options": {
"ident": "postcss",
"sourceMap": true,
"plugins": [
null
]
}
}
]
},
"ruleIndex": 4,
"loader": {
"loader": "css-loader",
"options": {
"modules": true,
"localIdentName": "[local]__[hash:base64:5]",
"importLoaders": 1,
"sourceMap": true
}
},
"loaderIndex": 2
},
{
"rule": {
"test": /\.(css|less|s[ac]ss|styl)$/,
"exclude": [
"C:\\glamst\\gst-new-moodmachine-web\\app\\src\\components",
"C:\\glamst\\gst-new-moodmachine-web\\app\\src\\routes"
],
"loader": [
{
"loader": "C:\\nvm\\v8.1.2\\node_modules\\preact-cli\\node_modules\\extract-text-webpack-plugin\\dist\\loader.js",
"options": {
"omit": 1,
"remove": true
}
},
{
"loader": "style-loader"
},
{
"loader": "css-loader",
"options": {
"sourceMap": true
}
},
{
"loader": "postcss-loader",
"options": {
"ident": "postcss",
"sourceMap": true,
"plugins": [
null
]
}
}
]
},
"ruleIndex": 5,
"loader": {
"loader": "css-loader",
"options": {
"sourceMap": true
}
},
"loaderIndex": 2
}
]
oh if the loader is already present modifying is a better thing.
Most helpful comment
As promised, here's a first pass at putting the above code into a plugin: https://github.com/matthewlynch/preact-cli-plugin-critical-css.
I've tested it in the project I'm currently building and it seems to be working as expected.
I'd love to hear any feedback you have so please let me know!