How to exclude specific files from caching - javascript

I'm using config-overrides.js with string-replace-loader in order to change the content of a file before being compiled by webpack.
But because of the caching system of webpack (cache option) the loader is not being executed, if I set cache = false everything works just fine (exactly like the production build).
I've spent several hours trying to understand how to exclude a file from caching, but whatever I've found does not work as expected and I'm kinda out of ideas.
Is there any way to achieve this without completely disabling the caching system?

[EDIT]
I know the question asks about files, but my problem was related to the index.js file which imports a component conditionally based on a specific value inside an external config file.
After some more couple of hours of investigating I've found what I was looking for.
Here is the official documentation regarding this exactly use case.
Basically what we can do to invalidate the cache is to use the cache object as below:
cache = {
type: 'filesystem',
version: `${any external variable/value which may change in the future}`, // Keep the `${}`
};
cache.version is a string. Passing a different string will invalidate the persistent cache.
I'm using it like this and it's working great so far.
const invalidateCacheValues = [
config.my_value_0,
config.my_value_1
];
webpackConfig['cache'] = {
type: 'filesystem',
version: `${invalidateCacheValues.join('|')}`,
};

Related

Webpack - Bundling legacy files that depend on each other globals

I'm working with a legacy project and I try to convert it to a modern webpack project, with minimum changes to the original legacy files.
The problem is that many of these legacy files rely on each other's globals, e.g:
legacy1.js
console.log("Legacy1");
function globalMess() {
console.log("I am doing global mess!")
}
legacy2.js
console.log("Legacy2");
globalMess();%
The ideas solution would be one that:
Allows all the legacy files use each other globals without me searching for every global.
The globals would be only global to the legacy files, and not leak out to the real global scope.
Allow me to use Webpack's cache busting hash on the resulting legacy file. So that if I do I fix a bug in one the legacy files, the resulting file would have a new hash (e.g legacy1.abc.js).
Automatically add the resulting file to index.html usings something like HtmlWebpackPlugin.
What I've considered so far
These are solutions I've considered:
Solution 1
Simply inject them manually into index.html, in the same order they appear in the original project. This solution would have no cache busting and would leak globals.
Solution 2
Using ExposeLoader/ProvidePlugin. Those require me know specify each and every global that each library exposes. This doesn't make sense to me as I have 20 legacy files and each exposes some random function and I don't really know which exposes what.
Solution 3
Using webpack-raw-bundler. It concats the files, pretty much like I wanted. However, it doesn't support cache busting via hash or automatically adding itself to index.html.
Ideal solution
So what I imagine as an ideal solution would be a webpack plugin that globs all the legacy files, bundles them all as into a single file in its own function and produces a single file with a hash. That file will also get injected into the index.html using the logic of HtmlWebpackPlugin.
Its usage would look something like that:
new LegacyBundlePlugin({
uglify: false,
sourceMap: false,
name: "legacy",
fileName: "[name].[hash:8].js",
filesToConcat: ["./legacy1.js", "./legacy2.js"],
}),
Which would produce the following:
(function () {
console.log("Legacy1");
function globalMess() {
console.log("I am doing global mess!")
}
console.log("Legacy2");
globalMess();
})();
Currently the best solution I found is using webpack-concat-plugin.
It bundles everything to a single file, allows me to use cache busting and adds itself via HtmlWebpackPlugin to the resulting html.
The only thing it doesn't do is not-leaking to the global scope as it simply concats the provided files and doesn't put them into any scope.

dynamically load modules in Meteor node.js

I'm trying to load multiple modules on the fly via chokidar (watchdog) using Meteor 1.6 beta, however after doing extensive research on the matter I just can't seem to get it to work.
From what I gather require by design will not take in anything other than static strings, i.e.
require("test/string/here")
Since if I try:
var path = "test/string/here"
require(path)
I just get Error: Cannot find module, even though the strings are identical.
Now the thing is I'm uncertain how to go on about this, am I really forced to either use import or static strings when using meteor or is there some workaround this?
watchdog(cmddir, (dir) => {
match = "." + regex_cmd.exec(dir);
match = dir;
loader.emit("loadcommand", match)
});
loader.on('loadcommand', (file) => {
require(file);
});
There is something intrinsically weird in what you describe.
chokidar is used to watch actual files and folders.
But Meteor compiles and bundles your code, resulting in an app folder after build that is totally different from your project structure.
Although Meteor now supports dynamic imports, the mechanism is internal to Meteor and does not rely on your actual project files, but on Meteor built ones.
If you want to dynamically require files like in Node, including with dynamically generated module path, you should avoid import and require statements, which are automatically replaced by Meteor built-in import mechanism. Instead you would have to make up your own loading function, taking care of the fact that your app built folder is different from your project folder.
That may work for example if your server is watching files and/or folders in a static location, different from where your app will be running.
In the end, I feel this is a sort of XY problem: you have not described your objective in the first place, and the above issue is trying to solve a weird solution that does not seem to fit how Meteor works, hence which may not be the most appropriate solution for your implicit objective.
#Sashko does a great job of explaining Meteor's dynamic imports here. There are also docs
A dynamic import is a function that returns a promise instead of just importing statically at build time. Example:
import('./component').then((MyComponent) => {
render(MyComponent);
});
The promise runs once the module has been loaded. If you try to load the module repeatedly then it only gets loaded once and is immediately available on subsequent requests.
afaict you can use a variable for the string to import.

Custom script and css versioning best practices and automation with gruntjs

I've decided to start versioning the custom css and js files i use in my WP themes so that when I update a site a page reload is not needed to display the changes to users(long overdue, I know).
Is just adding a version number as a query string at the end of the call and keeping the real file name static a good practice(as in "script.js?v=xyz")? How will this affect browser caching when the version number is not changed? Will it cache normally and only get a new version when i change the version number?
If the above is the way to go, i would like to automate this with Grunt so when I run my "build" command the PHP file where the files get enqueued in WP gets modified to contain a random hash + the date as the version parameter in the wp_enqueue_script command. I presume that when i run my development command with livereload and everything, the scripts having said version will not affect livereload so i do not need to remove the versioning, correct?
How would you do this, what npm module would you use and what would be the commands i need to add to my Gruntfile.js?
As a sidenote, i know that the version number added by wordpress by default to all scripts and css is considered a security risk as it exposes the WP version you're rolling and i've been removing it via functions.php, but, i would like to enable it, yet have it not use the WP version but something random that gets changed with each WP update. Any idea how one would implement this? I'm surprised that i was not able to find a plugin that does this.
Thank you ver much, community!
I have certainly done this, as a way of indicating to browsers that they should download a newer version of a stylesheet. This isn't particularly elegant, but one thing I've done is declared cache-busting parameter (xyz in your example) as a PHP constant:
// inc/constants.php
define('CACHEPARAMETER', '20161027');
And my grunt build task modifies this using grunt-text-replace:
replace: {
cacheparam: {
src: ['inc/constants.php'],
overwrite: true, // overwrite matched source files
replacements: [{
from: /'CACHEPARAMETER', '[0-9]{8}'/g,
to: "'CACHEPARAMETER', '<%= grunt.template.today('yyyymmdd') %>'"
}]
}
}
I'm enqueueing this in functions.php, which I do like this:
include "inc/constants.php";
wp_enqueue_style( 'my-style', get_stylesheet_uri(), array(), CACHEPARAMETER);
Hope this helps.

Why can I not use a variable as parameter in the require() function of node.js (browserify)?

I tried something like:
var path = '../right/here';
var module = require(path);
but it can't find the module anymore this way, while:
var module = require('../right/here');
works like a charm. Would like to load modules with a generated list of strings, but I can't wrap my head around this problem atm. Any ideas?
you can use template to get file dynamically.
var myModule = 'Module1';
var Modules = require(`../path/${myModule}`)
This is due to how Browserify does its bundling, it can only do static string analysis for requirement rebinding. So, if you want to do browserify bundling, you'll need to hardcode your requirements.
For code that has to go into production deployment (as opposed to quick prototypes, which you rarely ever bother to add bundling for) it's always advisable to stick with static requirements, in part because of the bundling but also because using dynamic strings to give you your requirements means you're writing code that isn't predictable, and can thus potentially be full of bugs you rarely run into and are extremely hard to debug.
If you need different requirements based on different runs (say, dev vs. stage testing vs. production) then it's usually a good idea to use process.env or a config object so that when it comes time to decide which library to require for a specific purposes, you can use something like
var knox = config.offline ? require("./util/mocks3") : require("knox");
That way your code also stays immediately traversable for others who need to track down where something's going wrong, in case a bug does get found.
require('#/path/'.concat(fileName))
You can use .require() to add the files that you want to access calculating its path instead of being static at build time, this way this modules will be included and when calling require() later they will be found.

require.js - How can I set a version on required modules as part of the URL?

I am using require.js to require JS modules in my application.
I need a way to bust client cache on new JS modules, by way of a different requested URL.
i.e., if the file hello/there.js has already been cached on the client, I can change the file name to force the browser to get the new file.
In other words, for the module hello/there, I'd like require.js to request the url hello/there___v1234___.js (the file name can look different, it's just an example), according to a version string which is accessible on the client.
What is the best way to achieve that?
I got really frustrated with the urlArgs solution and finally gave up and implemented my own fix directly into require.js. This fix implements your ideal solution, if you are willing to modify your version of the library.
You can see the patch here:
https://github.com/jbcpollak/requirejs/commit/589ee0cdfe6f719cd761eee631ce68eee09a5a67
Once added, you can do something like this in your require config:
var require = {
baseUrl: "/scripts/",
cacheSuffix: ".buildNumber"
}
Use your build system or server environment to replace buildNumber with a revision id or software version.
Using require like this:
require(["myModule"], function() {
// no-op;
});
Will cause require to request this file:
http://yourserver.com/scripts/myModule.buildNumber.js
The patch will ignore any script that specifies a protocol, and it will not affect any non-JS files.
On our server environment, we use url rewrite rules to strip out the buildNumber, and serve the correct JS file. This way we don't actually have to worry about renaming all our JS files.
This works well for my environment, but I realize some users would prefer a prefix rather than a suffix, it should be easy to modify my commit to suit your needs.
Here are some possible duplicate questions:
RequireJS and proxy caching
Prevent RequireJS from Caching Required Scripts
OK, I googled "requirejs cache bust" for you and found this existing SO answer, which says you can configure requireJS with an urlArgs parameter, which is only a partial solution, but it might be enough to meet your immediate needs.
That said, the cachebusting problem is full of challenges and many "solutions" don't actually solve the problem in its entirety. The only maintainable way to do this (IMHO as of now) is with a full-on asset management system like the Ruby on Rails asset pipeline or connect-assets or the equivalent for your server side framework of choice. Those can correct compute a checksum (usually MD5 or SHA1) of the content of each file and give you the file names you need to put as URLs in your HTML script tags. So, don't bother with manually changing filenames based on version numbers, just use checksums since they are easily automated and foolproof.
From what I can tell, out of the box requirejs can't do the cachebusting aspect for you. You might want to read this google groups thread. Otherwise, you may need to pair requirejs with an additional tool/script to get you good cachebuster checksums.
Just do it like the creator of requirejs suggests:
var load = requirejs.load;
requirejs.load = function (context, moduleId, url) {
// modify url here
url = url.substring(0, url.lastIndexOf('.')) + '.' + VERSION + url.substring(url.lastIndexOf('.'));
return load(context, moduleId, url);
};
https://github.com/jrburke/requirejs/wiki/Fine-grained-URL-control
HTML5 Boilerplate has ant-build-script that renames your files and any reference to them for this exact reason and can do alot more. It's worth checking out if you haven't already.

Categories

Resources