How to make NextJS inject styles into head in production? - javascript

By default, NextJS will inject <style> tags inline inside head in development (possibly using style-loader under the hood).
In production mode, NextJS will extract css chunks and serve them as separate css files from _next/static directory.
I debugged webpack config NextJS serves by default and noticed it uses mini-css-extract-plugin under the hood to achieve this behavior.
The problem is, for my needs I need NextJS to inject inline styles in production as well. What is the easiest way to achieve this?
Here is the current next.config.js that I use
const nextConfig = {
useFileSystemPublicRoutes: false,
poweredByHeader: false,
assetPrefix: '',
webpack(config) {
console.dir(config.module.rules, { depth: null, colors: true });
config.module.rules.push({
test: /\.svg$/,
use: ['#svgr/webpack'],
});
return config;
},
};
module.exports = nextConfig;

You can use next/head to append <head>.
https://nextjs.org/docs/api-reference/next/head
By the way; inline-styles may be understood as <div style=".." /> I think you are asking style tags inside head; to avoid confusion; you can edit your post to clarify that little bit.

Related

Issue with ESLint in Nuxt

I'm learning Nuxt. I've set up a project, with ESLint included. Here's my index.vue:
<template>
<h1 class=reddo>Hello, world</h1>
</template>
<script>
export default {
head() {
// Set Meta Tags for this Page
}
// ...
}
</script>
<style>
.reddo {
color: red;
}
</style>
When I run this I get this:
(It doesn't say this is from ESLint, but I assume it is). The first error complains about the indentation before the <h1>. Do I need to do something to make it understand .vue files so it validates only the <script> part or something?
Thank you in advance.
Since you followed the Nuxt intro video, you likely have a .eslintrc.js file in your project folder that extends a config from #nuxt/eslint-config that adds this parser and some default rules (source code).
The default configuration is linting the entire .vue file, as the Vue parser understands them. There are some limitations to the parser as well which may be triggering your linter if there were any changes not from the Nuxt config.
You can change or disable this configuration by editing your .eslintrc.js file; however, there are many advantages to statically analyzing your code using a linter. So consider finding or making a config that has few stylistic rules (or ones that you like) so you can still catch possible errors, including ones specific to Vue.
If you want to revert to a working .eslintrc.js file, try copying the changes from a new create-nuxt-app.
module.exports = {
root: true,
env: {
browser: true,
node: true
},
parserOptions: {
parser: 'babel-eslint'
},
extends: [
'#nuxtjs',
'plugin:nuxt/recommended'
],
plugins: [
],
// add your custom rules here
rules: {}
}

How to change the src attribute of <img> in .vue files with webpack and vue-loader?

I have a vue.js (version 2.4.4) application built with webpack (version 3.6.0) and vue-loader (version 13.0.5).
In the .vue files, I need to modify the url contained in the src attribute of the <img> tags according to the environment of my application.
In the development environment, the images must come from the
application folder, with a relative path: "/src/images/exemple.png"
In the production environment, the images must come from a cdn, with
an absolute path: "https://my-cdn.net/images/exemple.png"
In the "webpack.config.js" file, I already differentiate the different environments using "process.env.NODE_ENV", like this:
const isDev = process.env.NODE_ENV === 'dev';
But I don't know how to modify the src attribute of the img tags in my .vue files with vue-loader (or with something else).
For information, here is the definition of the vue-loader in the "webpack.config.js" file:
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
loaders: {
'scss': [
'vue-style-loader',
'css-loader',
'sass-loader'
]
}
}
}
Is there a simple way to do this?
Piggybacking off of #Michael Levy's answer:
I am currently having this issue with Vue 3, #vue/cli 4.5.10, and webpack. I've solved it after much research.
Webpack configurations go into vue.config.js, where there is a lot of abstraction. To fine tune control, you can use chain webpack configs. To help you, use Vue Inspect when you are trying to access specific loaders via chaining.
$ vue inspect > output.js
That will give you a nice list of all the loaders that vue-cli is using.
For example - to modify webpack image options within vue.config.js, you can use vue inspect > output.js, search the output.js file, and discover the loader that's managing images.
Which is: /* config.module.rule('images').use('url-loader') */
To answer the question - in your vue.config.js
module.exports = {
chainWebpack: (config) => {
config.module
.rule("images")
.use("url-loader")
.tap((options) => {
options.name = "images/[name].[ext]";
options.publicPath = isDev ? __webpack_public_path__ : 'https://my-cdn.net/';
return options;
});
},
};
Vue-loader is preconfigured to handle src attributes in Vue single file components as you can see here. So for example <img src="../image.png"> in the template is transformed into:
createElement('img', {
attrs: {
src: require('../image.png') // this is now a module request
}
})
What Webpack do with this require depends on configured loaders. Usual there is a file-loader configured. It looks like this (from project generated by Vue CLI + simplified):
module: {
rules: [
{
test: /\.(png|jpe?g|gif|webp)(\?.*)?$/,
use: [
{
loader: 'file-loader',
options: {
name: 'img/[name].[hash:8].[ext]'
}
}
]
}
]
}
Loader is responsible for copying your file into dist directory and returning public URI, which will be inserted into src attribute.
So what you want can be configured here, by specifying right options. For example:
options: {
name: 'images/[name].[ext]'
publicPath: isDev ? __webpack_public_path__ : 'https://my-cdn.net/'
}
Just take content of dist/images directory after the build and deploy it so it is accessible by https://my-cdn.net/images and it should work....
You can create an alias for/src/images and alter the url at transpile time based on the environment:
{
//..
resolve: {
alias: {
'/src/images': isDev ? '/src/images' : process.env.IMAGE_CDN + '/images'
}
}
}
Another way to handle this would be to use DefinePlugin to create a global variable that each of your images reference.
module.exports = {
chainWebpack: config => {
console.log('\n')
console.log('Setting global variables:')
console.log(`__YOUR_GLOBAL_CONSTANT__: ${JSON.stringify(process.env.YOUR_GLOBAL_CONSTANT)}`)
console.log('\n')
config
.plugin('provide')
.use(require('webpack').DefinePlugin, [{
__YOUR_GLOBAL_CONSTANT__: JSON.stringify(process.env.YOUR_GLOBAL_CONSTANT)
}])
}
}
The example above is utilizing a vue.config.js file, but the strategy should be pretty similar. Also, if you're using something like eslint, you'll need to specify the variable in the globals section as readonly.

How to transpile simple react component in express workspace

I have a fairly simple question.
I was writing simple express page for company (basic table delete row button and form page)
easyyyy
But I decided to spruce form page a little bit with ajax validation and dynamic fields. Since I did few full react sites before i thought making this component would be easy. And tbh making component is easy but babel and webpack is not.
is there any easy way to transpile jsx with imports to the web without configuring whole separate workspace?
If react for this overkill and jQuery fell out of grace is there any other suitable library?
Hope you can understand my broken English
yes react is powerful, you can add it and setup it like,
you can create the component separately and load the script on your target page just make sure you place the mount point right
and you can use create react app with this as well
just build the project place the bundle js path on the same page
and add the mount point and it should work smoothly
<div id="mountPoint" ></div>
another way like the react does suggest here
I did minimal configuration of webppack and babel. When it wasnt so bad still configuring two libraries for one small component with 2 dependencies seems ridiculous :P
My config maybe somebody finds it useful:
webpack.config.js
const path = require('path');
module.exports = {
mode: 'development',
output: {
filename: 'public/javascripts/dist.js'
},
module: {
rules: [
{
test: /\.jsx$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env', "#babel/preset-react"]
}
}
}
]
}
};
babel.config.js
module.exports = function (api) {
api.cache(true);
const presets = [];
const plugins = ['#babel/plugin-proposal-class-properties'];
return {
presets,
plugins
};
}

How to stop purging tailwind components

I am using TailwindCSS and Laravel Mix. I am trying to setup PurgeCSS and I have got it reading my template files (working with WordPress) and purge any CSS not within the template files.
However, as part of Tailwind, I am using #apply in my scss files and those utilities I am applying are also being purged which leaves me with a non functioning site.
My sass files are in css/dev and there is an app.scss and then directories with more files within them (base, utilities, custom, components).
My webpack.mix.js file configuration is as follows:
mix.scripts(['js/dev/app.js', 'js/dev/navigation.js', 'js/dev/skip-link-focus-fix.js'],
'js/build/app.js')
.sass('css/dev/app.scss', 'css/build')
.options({
processCssUrls: false,
postCss: [tailwindcss('./tailwind.config.js')],
})
.purgeCss({
enabled: mix.inProduction(),
// Your custom globs are merged with the default globs. If you need to
// fully replace the globs, use the underlying `paths` option instead.
globs: [
path.join(__dirname, 'template-parts/*.php'),
path.join(__dirname, '*.php'),
path.join(__dirname, 'css/dev/*.scss'),
path.join(__dirname, 'css/dev/**/*.scss'),
],
extensions: ['html', 'js', 'php', 'scss', 'css'],
});
As you can see, I tried setting the paths for purgeCss to look inside the css paths but that has not worked.
Does anyone know how to achieve this?
You are compiling your scss to css before Purge runs, so there should be no need to purge your .scss files only your main.css (or whatever the output is called).
Do your compiled class names actually exist in full in your template files? If they are not a 100% match for the classes in your templates then they will, quite correctly, be purged.
The issue was with WordPress classes not being included in the template files and then being purged. The solution was switching to using UnCSS which allowed me to setup URLS for UnCSS to visit and it won't purge any classes used on those pages. I also included some standard WordPress classes which I found a list of online.
My final config is:
const uncss = require('postcss-uncss');
mix.js('js/dev/app.js', 'js/build/app.js')
.sass('css/dev/app.scss', 'css/build')
.options({
processCssUrls: false,
postCss: [
tailwindcss('./tailwind.config.js'),
...process.env.NODE_ENV === 'production' ? [uncss({
html: [
'./*.php',
'./template-parts/*.php',
'https://www.example.com',
'https://www.example.com/work/',
'https://www.example.com/work/example-project/',
'https://www.example.com/contact/',
'https://www.example.com/blog/',
'https://www.example.com/blog/laravel-php-framework/',
],
ignore: [
'.rtl',
'.home',
'.blog',
'.archive',
'.date',
'.error404',
'.logged-in',
'.admin-bar',
'.no-customize-support',
'.custom-background',
'.wp-custom-logo',
'.alignnone',
'.alignright',
'.alignleft',
'.wp-caption',
'.wp-caption-text',
'.screen-reader-text',
'.comment-list',
'.grecaptcha-badge',
/^search(-.*)?$/,
/^(.*)-template(-.*)?$/,
/^(.*)?-?single(-.*)?$/,
/^postid-(.*)?$/,
/^attachmentid-(.*)?$/,
/^attachment(-.*)?$/,
/^page(-.*)?$/,
/^(post-type-)?archive(-.*)?$/,
/^author(-.*)?$/,
/^category(-.*)?$/,
/^tag(-.*)?$/,
/^tax-(.*)?$/,
/^term-(.*)?$/,
/^(.*)?-?paged(-.*)?$/,
'.animate',
'.animated',
'.bounce',
'.fadeInDown',
'.fadeIn',
'.fadeInUp',
'.jackInTheBox',
]
})] : [],
]
});
I also made use of the UnCSS exclude from purge CSS comments:
/* uncss:ignore start */
my css goes here
/* uncss:ignore end */
I ended up using this on all my custom sass files except for the tailwind files so that the only selectors that are purged are tailwind utilities, which saved me about 300 KB.

Render multiple pages unrelated to the main app with Webpack and Mustache

I'm developing a Chrome Extension and I use Webpack to bundle it. I've got my compiled bundle, which is the main part of the app, but I also need an options page to describe the functionality. This options page has nothing to do with the bundle, it's just a static HTML file.
I must put a lot of things in that options page so I want to render that page with Mustache and define all content with JavaScript. For the most part, I've done that.
Here's my Webpack config (I've removed the parts regarding my app):
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
output: {
path: path.join(__dirname, 'extension/build/')
},
plugins: [
new HtmlWebpackPlugin({
template: './src/options/index.html',
inject: false
})
],
module: {
rules: [
{
test: /\.html$/,
loader: 'mustache-loader',
options: {
render: require('./src/options/index.js')
}
}
]
}
}
and in my src/index.js, I have:
require('./options/index.html')
This will open the template and render it with the data in src/options/index.js.
There's a problem with that, however. I run Webpack with webpack --watch and changes to index.js (it holds the template data) do not trigger a rebuild. Also, I would need to go through a lot of trouble to create another static HTML file in the same manner.
It would be ideal if HtmlWebpackPlugin automatically used the template I require() in my entry point so that I don't need to explicitly set it. Also, it would be great if it automatically used a js in that same location to get the data. For example:
require('./options/index.html`)
Renders the template with data from ./options/index.html.js and then emits it. It would be even better if it emitted it to a custom folder specified in the Webpack config.
Is that possible? I couldn't find a plugin/loader that does that.
Edit: I was able to partly fix the rebuild problem by specifying the render option as a function:
{
test: /\.html$/,
loader: 'mustache-loader',
options: {
render () {
var file = './src/options/index.js'
delete require.cache[require.resolve(file)]
return require(file)
}
}
}
But it still doesn't work properly. The rebuild would only trigger after I make changes to index.html. This means that if I change index.js, I need to go and save index.html as well to trigger the build.

Categories

Resources