Multiple extract-text-webpack-plugin instances - javascript

Multiple extract-text-webpack-plugin instances
I want to do the following thing:
Merge many SVGs in a single sprite (done via svg-sprite-loader).
Extract sprite as separate file (done via svg-sprite-loader/lib/extract-svg-plugin, tiny wrapper over the extract-text-webpack-plugin which just wraps result with tags to get valid svg markup).
Return path to extracted SVG with fragment identifier which refers to specific sprite symbol, e.g. sprite.svg#image. This doable unless you want to refer SVG image (which extracts via extract plugin) in CSS which also extracts via second extract plugin instance.
Example (full example on GitHub):
webpack.config.js
var TextExtractPlugin = require('extract-text-webpack-plugin');
var CSSExtractor = new TextExtractPlugin('[name].css');
var SVGExtractor = new TextExtractPlugin('[name].svg');
module: {
loaders: [
{
test: /\.css$/,
loader: CSSExtractor.extract('css')
},
{
test: /\.svg$/,
loader: SVGExtractor.extract('raw') // emulate svg-sprite-loader
}
]
}
styles.css
.img {
background-image: url('./image.svg');
}
CSS will be compiled to
.img {
background-image: url([object Object]);
}
I think it's because in first (SVG) extract compilation all image source was removed by extract-plugin, and in the second (CSS) extract compilation get's cached module result from webpack with empty content. I trying to patch extract plugin loader and return some result instead of // // removed by extract-text-webpack-plugin, but has no success. In second extract compilation image module still has empty content.
Is it possible to implement desired behaviour with extract plugin?
I've created GitHub repo with example.

Related

Get original file path from within Webpack loader

I'm building a custom Webpack loader. What the loader does is unimportant, but it transforms JSON in some way and uses paths from the JSON in order to resolve certain other details. In my loader.js I need a way of getting the original path of the JSON file being loaded such that I can resolve other paths properly.
Take this simple loader and config:
loader.js
module.exports = function (source) {
/* Do some file lookups based on source and modify source */
this.callback(null, source)
}
webpack.config.js
module.exports = {
/* ... */
module: {
rules: [
{
test: /\.json$/i,
use: ['loader'],
},
],
},
};
The loader is working (being used), but any business logic I add needs to be path-aware such that it can do lookups on the file system.
Because this is a JSON loader, the source var in the loader function is passed as raw JSON content, with no details about which file the JSON was loaded from. How does one go about getting path information from within the loader such that I can perform other file lookups based on the content from source?
It turns out the property I was looking for is available via the execution context property this, as resourcePath.
module.exports = function (source) {
console.log('The original file was here:', this.resourcePath)
this.callback(null, source)
}
Here is the (rather sparse) documentation.
If anybody can think of a better/more future-proof way of getting this path then feel free to comment or post another answer.

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.

How to preserve folder structure using file-loader with webpack, but skip "src"?

I'm using file-loader with Webpack 4 and I'm copying some resources as is to the /dist folder. It's possible by providing
name: "[path][name].[ext]"
as a parameter, but this results in preserving the whole folder structure (in my case, also including src/ part). My goal is to copy the exact path of the files, but using src/ as a root, does anybody have any experience doing that?
Webpack file-loader accepts a function for a name property. You can use that to return custom computed path.
{
test: /\.(png|jpg|gif)$/,
use: [{
loader: 'file-loader',
options: {
// name can also be a simple string if it is only path values
name(file) {
// generate path dynamically here
const newPath = '[hash].[ext]';
return newPath;
},
},
}],
}
Also, if you really need to just copy assets from src to dist, then probably webpack-copy-plugin is a better way to handle it.

How can I recompile LESS manually in reactjs?

I have a ReactJs project which uses Webpack and Redux. I am using less for my stylesheets. In this project, I have colors.less file which looks like this -
//colors.less
#color1: red;
....
This file is imported to all the less files who are using this variable.
What I want is change this #color1 variable according to some API data and then the stylesheets should update with the new color. I have access to the this variable in my JS file, but on changing this color I want to reload the stylesheets as well.
Accessing the variable like below -
//utils/less-var-loader.js
const lessToJs = require('less-vars-to-js')
module.exports = function(content) {
return `module.exports = ${JSON.stringify(lessToJs(content))}`
}
//some.js which wants to modify the color
import * as styles from '!!../utils/less-var-loader!./common/colors.less'
styles['#color1'] = blue;
First add the below dependencies for webpack.
Add the less dependencies
webpack.config.js
,{
test: /\.less$/,
loaders: ['style', 'css', 'less']
}
Require it at your entry point.

How can we conditionally load a vendor css with webpack?

For example, if we have a vendor scss file named vendor.scss with the following content:
$color: green;
h1 {
background-color: $color;
}
How can we write the rules to achieve to following result? (Or similar)
let vendor = true; // this will be configured outside
if (vendor) {
let style = require('style-loader/useable!css-loader!./vendor.scss');
style.use(); //loads just if I call `use`, otherwise not
}
require('my-other-file.scss'); // loads normally, without `use`.
Because vendor.scss is a vendor scss/css we cannot or should not rename it to, for example, vendor.useable.scss.
And for maintaining purposes we would like to not create a new one like my-vendor-wrapper.scss that could wrap it with an #import inside of it.
In webpack, for now, I have this rule:
const ExtractTextPlugin = require('extract-text-webpack-plugin')
module.exports = {
// test: /\.(scss|sass|css)$/i,
test: /^(?!.*style-loader\/useable!).*\.(sass|scss|css)$/i,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: ['css-loader', 'postcss-loader', 'sass-loader']
})
}
I tried several things, like creating additional rules.
If I use an custom extension like lscss (for lazy scss) I can put it to work.
But the expected behavior that I would like is load all css/scss normally but if it have style-loader/useable! in front of it just loads if I call use.
How can we do this?

Categories

Resources