I'm trying to pass less variables within a webpack configuration to the less loader, naturally.
For some reason the variable is not being passed ok. I can't figure the correct syntax.
The variable has a dynamic content that is determined at the build time, in the webpack config file. This is the relevant line (I've tried many variations of it):
loader: 'style!css?-minimize!less?-minimize&{modifyVars:{"resources-path-prefix":"' + pathPrefix + '"}}'
In the above example some pathPrefix is being determined at build time and we want to pass its value into less context, where it will be used in url() css directives.
The above doesn't work - nothing is passed into less, and the default variable value defined in less applies.
Can anyone show how to correctly pass the value into the less compilation process? Thanks!
So it was tough but we finally made it work(!). Arggh - so much time invested to trying and figure out the syntax.
Here's the task: we want at build time to determine a path that should use used as the base url for misc assets in less files (background images, using url() less function).
First, we determined the path in webpack config file. Its plain JS, but the escaping pattern for the path string was absolutely nuts. We probably invested hours just on this. Amazing. Here it is:
var assetsPath = (someCondition) ? '/assets' : "\\/127.0.0.1:8080/assets";
Next is the loader configuration for less files, using the assetsPath prefix set above:
{
test: /\.less$/,
exclude: /node_modules/,
loader: 'style!css?minimize=false!less?{"modifyVars":{"assetspath":"\'' + assetsPath +'\'"}}'
}
Notice the escaping pattern above where using the assetsPath in the loader configuration.
Next, you need to make sure that an empty variable is being defined in the less files. We initialized it in our 'vars.less' file, with:
#assetspath: '';
Finally, in any relevant class, we can use the value being passed in build time like this:
background-image: url("#{assetspath}/images/facebook.png");
You can try to use the query section of the loader:
loader: 'style!css?-minimize!less?-minimize',
query: {
modifyVars: {
"resources-path-prefix": pathPrefix
}
}
It's a different approach to this situation, but we managed to make things work by converting into Base64 every resource that the CSS files loaded. We had to do this because figuring out the hostname of the resources was possible much later down the line than the webpack config file.
Related
I was able to output an assets library and many other libraries that work as remote federated modules and as deep import libraries in case I am not connecting to a remote or I am not using webpack in the consumer end.
The issue is now that all my assets exports a module, that either have the raw data as uri or the string that points to the right asset. Eg: the bird.svg is outputed to dust with it's hash plus the modules that resolves to the file bird-[hash].svg.
The above is great from javascript but not so much for css. Now I can't rewrite the url() to point to the right remote path which would be sg like:
//since I don't know when the assets will change names I can't refer to it directly. So I would need to first read the string from the bird.js module. Append the publicPath and then rewrite the url.
.someClass {
background-image: url('/assets/bird.js')
}
//the above won't work for obvious reasons.
Só, the question is how can I solve this? Or is there any loader for this? I checked the resolve url loader but it does not seem to be what need.
Ok I resolved this issue, by passing additional data as variable to sass-loader. That way I can evaluate the actual name of the files, and put it as a sass map before and handle it from sass.
//I am using glob to return an object with all the assets.
//This can probably be automated better. That would be an easier way.
//But this way works for me in all 3 scenarios, node, browser and federated module.
//Also has caching ootb for the assets.
const assetsPaths = {
...glob.sync('../assets/dist/img/**.node.js').reduce(function (obj, el) {
obj['img/' + path.parse(el).name] = '../../assets/dist/' + require(el).default;
return obj
}, {}), ...glob.sync('../assets/dist/fonts/**.node.js').reduce(function (obj, el) {
obj['fonts/' + path.parse(el).name] = '../../assets/dist/' + require(el).default;
return obj
}, {})
};
//...
{
loader: 'sass-loader',
options: {
additionalData: "$assets: '" + assetsMap + "';",
sourceMap: true,
sassOptions: {
outputStyle: "compressed",
},
}
},
//...
you also need to disable url rewriting
{
loader: 'css-loader',
options: {
url: false,
}
},
then you can use assets map in your sass files:
#font-face {
font-family: 'Some Font';
src: local('Some Font');
src: url("#{map-get($assets, SomeFont)}");
}
You will need probably have your project setup sort like a mono repo and you also need to build those assets library with two bundles.
One for node so you can use the string path to your actual assets when bundling you sass/whatever.
And another for normally loading it from the browser.
update:
Instead of doing all this I just used the manifest generated from 'webpack-manifest-plugin' to build the $assets map to be used in sass.
const assetsManifest = JSON.parse(fs.readFileSync('../assets/dist/manifest.json'));
const assetsMapFn = asset => `'${asset[0]}':'${asset[1]}'`;
const assetsMap = `(
${Object.entries(assetsManifest).map(assetsMapFn).join(',')}
); `;
If anyone knows a better way to do this please reply or comment.
I'm trying to write tests for a react/redux app, and we have a bunch of webworkers which are currently imported via require('worker-loader?inline!downloadTrackWorker')
I've been going in circles trying to figure out how to separate out this code so I can run tests in node.js without having trouble with loading the webworker.
One solution I came up with was to expose the webworker globally in my webpack, which would mean I could define a stub or mock in my tests.
In my webpack config, I've added
module: {
loaders: [...],
rules: [{
test: require.resolve(APP_DIR + '/helpers/downloadTrackWorkerLoader'),
loader: 'expose-loader',
options: 'DownloadTrackWorker'
}]
}
my trackWorkerLoader is simply
const DownloadTrackWorker = require('worker-loader?inline!./downloadTrackWorker.js');
module.export = DownloadTrackWorker;
I've also tried the above without inline, but no luck.
I'm experiencing two problems.
when I look for DownloadTrackWorker in my console, it is undefined
with my updated webpack.config, I get an error that webpack can't parse may need appropriate loader at
ReactDOM.render(
<Provider store={store}>
<Player />
</Provider>,
document.getElementById('root')
);
Any suggestions on what I'm doing wrong? It appears to me the issues I'm seeing are related.
when I look for DownloadTrackWorker in my console, it is undefined
As the expose-loader notes in Readme - Usage, you need to import it in order to be included in the bundle and therefore exposed. The rules are not including anything but are applied to the imports in your app which satisfy the test. Besides that you're also not applying the loader to the correct file. You want to apply the expose-loader to trackWorkerLoader.js, so the correct rule would be:
{
test: require.resolve(APP_DIR + '/helpers/trackWorkerLoader'),
loader: 'expose-loader',
options: 'DownloadTrackWorker'
}
Now you need to import it somewhere in your app with:
require('./path/to/helpers/trackWorkerLoader');
This will correctly expose DownloadTrackWorker as a global variable, but you have a typo in trackWorkerLoader.js instead of module.exports you have module.export. Currently you're not actually exporting anything. It should be:
module.exports = DownloadTrackWorker;
Instead of inlining the worker-loader in the require (not talking about its option) you can also define it as a rule:
{
test: require.resolve(APP_DIR + '/helpers/downloadTrackWorker'),
loader: 'worker-loader',
options: {
inline: true
}
}
And now you can simply require it without needing to specify the loaders in trackWorkerLoader.js:
const DownloadTrackWorker = require('./downloadTrackWorker');
module.exports = DownloadTrackWorker;
with my updated webpack.config, I get an error that webpack can't parse may need appropriate loader
You're defining both module.loaders and module.rules at the same time. Although module.loaders still exists for compatibility reasons, it will be ignored completely if module.rules is present. Hence the loaders you configured before, are not being applied. Simply move all rules to module.rules.
We're using the style-loader in webpack, which, when compiled seems to place information about the current directory in the source code that injects style tags when modules are loaded/unloaded. It looks roughly like this:
if(false) {
// When the styles change, update the <style> tags
if(!content.locals) {
module.hot.accept("!!./../../../../../node_modules/css-loader/index.js!./../../../../../node_modules/sass-loader/index.js?includePaths[]=/var/deploy/referrals/web_head/releases/20151118202441/node_modules/patternity/node_modules/node-neat/node_modules/node-bourbon/node_modules/bourbon/app/assets/stylesheets&includePaths[]=/var/deploy/referrals/web_head/releases/20151118202441/node_modules/patternity/node_modules/node-neat/node_modules/bourbon-neat/app/assets/stylesheets&includePaths[]=/var/deploy/referrals/web_head/releases/20151118202441/node_modules/patternity&includePaths[]=/var/deploy/referrals/web_head/releases/20151118202441/node_modules/infl-fonts!./campaigns.scss", function() {
var newContent = require("!!./../../../../../node_modules/css-loader/index.js!./../../../../../node_modules/sass-loader/index.js?includePaths[]=/var/deploy/referrals/web_head/releases/20151118202441/node_modules/patternity/node_modules/node-neat/node_modules/node-bourbon/node_modules/bourbon/app/assets/stylesheets&includePaths[]=/var/deploy/referrals/web_head/releases/20151118202441/node_modules/patternity/node_modules/node-neat/node_modules/bourbon-neat/app/assets/stylesheets&includePaths[]=/var/deploy/referrals/web_head/releases/20151118202441/node_modules/patternity&includePaths[]=/var/deploy/referrals/web_head/releases/20151118202441/node_modules/infl-fonts!./campaigns.scss");
if(typeof newContent === 'string') newContent = [[module.id, newContent, '']];
update(newContent);
});
}
// When the module is disposed, remove the <style> tags
module.hot.dispose(function() { update(); });
}
The thing to note is the directory listed in the accept string.
Now, this code ultimately gets removed once uglify is run (because of the if (false)) which is great. Where my problem lies however is that this compilation happens on 2 machines and the chunk hashing appears to happen before uglification, because the hash generated on 2 different machines (or even on the same machine but when in a different folder) is different. This obviously won't work if I'm deploying to, say, a production machine and need both of these machines to serve up an asset with the same digest.
Just to clarify, when not minified, the code is different, thus generates a different hash, but minification does in fact make the files identical, however the chunk hashing appears to have happened before minification.
Does anyone know how I can get the chunkhash to be generated after the uglify plugin is run. I'm using a config like so:
...
output: {
filename: '[name]-[chunkhash].js'
...
with the command:
webpack -p
edit
So after looking over this. I'm seeing now that this has to do with us adding includePaths to our style loader, it looks like this:
var includePaths = require('node-neat').includePaths;
module: {
loaders: [
{ test: /\.scss$/, loader: "style!css!sass?includePaths[]=" + includePaths },
]
}
So I think we know why we're getting these absolute URLs, but I think the original question still stands, IMO webpack should be hashing chunks AFTER minification, not before.
I'm trying to setup webpack for my project.
The project is big enough and is provided in multiple languages.
I want each of my entry points to be provided in each language as separate files. My language files are not just plain JSON, but JavaScript instead. So i18n plugin doesn't match my needs.
The solution seems to be similar to i18n plugin:
var languages = ['en', 'fr', 'de'];
module.exports = languages.map(function (lang) {
return {
name: lang,
// some other language-dependent config
}
})
Then in some of my scripts I want to require localization file, using environment variable:
var lang = ...; // some environment variable, available only at compile time
var l10n = require('./lang/' + lang);
But by default webpack tries to store that expression between parentheses assuming to evaluate it later in browser.
So is there a way to tell webpack to evaluate that immediately?
Or maybe someone has a better solution to my problem?
You should be able to use Webpack's DefinePlugin to set the language at compile time.
For instance, you could write your require as:
var l10n = require('./lang/' + APPLICATION_LANGUAGE);
and in your config, have
plugins: [
new webpack.DefinePlugin({
APPLICATION_LANGUAGE: JSON.stringify(lang)
})
]
You can have your build script conditionally set 'lang' based on some parameter or env variable or something.
I have a require.config in my main like the following.
require.config({
baseUrl:'scripts/',
paths:{
jquery:'shell/lib/jquery/jquery-1.7.1'
// many libraries and modules are aliased here
},
map:{
'*':{
'underscore':'shell/lib/underscore/underscore'
// a few other modules are mapped here
}
}
});
I did this because the files defined in map are using internal dependencies(in their respective folders) using relative paths.
Now when I run optimizer, the modules defined in path are saved as module IDs, like jquery saved as jquery while those in map are getting complete paths, like 'underscore' as 'shell/lib/underscore/underscore' instead of 'underscore'.
This is causing problems as I am using 'underscore' in other modules also and there the optimized file is having 'underscore' instead of 'shell/lib/underscore/underscore'.
Is there some specific way to optimize when we give map configs or something I am missing? Please tell me how to fix it.
Thanks
I'm not sure to understand the issue:
This is causing problems as I am using 'underscore' in other modules also and there the optimized file is having 'underscore' instead of 'shell/lib/underscore/underscore'.
This seems to be the expected behavior, you mapped underscore to that path for all modules. So basically you are telling to r.js: each time that you find the underscore dependency rewrite it to shell/lib/underscore/underscore. If your modules use "internal paths" and you want do do the opposite (make them to reference underscore), you need to do the opposite mapping:
'some/path/underscore': 'underscore'
In that case all the modules will be pointing to the same underscore module. Even those that use some strange path for underscore.
In the extreme case that you need to control how r.js writes modules on disk. You can use the onBuildWrite property (see https://github.com/jrburke/r.js/blob/master/build/example.build.js#L517).
For example:
onBuildWrite: function ( moduleName, path, contents ) {
if ( path === './src/somefile.js' ) {
return contents.replace(/^define\('src\/underscore'/, "define('underscore'");
} else {
return contents;
}
}
This example is a "hack" that tell to r.js: when you process somefile.js, replace src/underscore with underscore (is exactly what you do with map... but is just to show you how you can use onBuildWrite to do nasty things).