RequireJS automatic single file cache bust - caching

I've seen the many solutions for using urlArgs to force the file to change but is there an efficient way to have it automatically change only single files if the file has been updated?
Is it possible to bust the cache of only files that have been modified?
The biggest example being this topic.
function bust(path) {
return path + '?bust=' + (new Date()).getTime();
}
require.config({
baseUrl: '/base/path',
paths: {
'fileAlias': bust('fileLikelyToChange'),
'anotherFileAlias': bust('anotherFileLikelyToChange'),
'jQuery': 'jQuery'
},
});
The problem is this solution busts the cache every time instead of only when the file has been modified.

My solution may be a bit heavy handed and is a hybrid of this and other solutions posted but it works for me.
Part 1:
In the html, append all dependent scripts with cache busting query string-->
<script type="text/javascript">
var require = {
urlArgs : "bust="+ Math.random()
}
</script>
<script data-main="js/app" src="js/require.js"></script>
Part 2:
Then in app.js add a custom bust method to apply to only the files that may have changed:
function bust(path) {
return path + '.js?bust=' + Math.random();
}
Part 3:
update the urlArs with an empty string: urlArgs: "" to overide cache busting query string ubiquitously applied in Part 1 and add the cache busting method only to files of your choice:
requirejs.config({
urlArgs: "",
paths: {
'utilities': 'utilities',
'controller': bust('controller'),
'main': 'main'
}
});

Related

RequireJS data-main does not load from html files at different paths

Using RequireJS successfully for a single page application for quite some time. Finally adding JS unit tests to exercise model classes. I have a single config for src modules and another for test modules. (I want to keep test files out of the shipping distribution)
I can load src from index.html and test from LocationTest.html only if both files are in the same top-level folder. I would like to move LocationTest.html inside the test folder, but no change to data-main or the test config file seems to work.
Here is the file layout for the happy path...
/www
- index.html
- LocationTest.html
- js
- src
- lib
- require.js
- require_src_config.js
- require_main.js
- require_test_config.js
- require_test.js
- model
- Location.js
- (other src model classes)
- test
- lib
- (jasmine libs)
- model
- LocationTest.js
- (other test model classes eventually)
index.html
...
<script type='text/javascript' data-main='js/src/lib/require_main' src='js/src/lib/require.js'></script>
...
LocationTest.html
...
<script type='text/javascript' data-main='js/src/lib/require_test' src='js/src/lib/require.js'></script>
...
js/src/lib/require_src_config.js
requirejs.config({
// Base URL for main application
baseUrl: 'js',
// Shortcuts to modules relative to baseUrl
paths: {
Location: 'src/model/Location'
(many other modules...)
}
});
js/src/lib/require_test_config.js
requirejs.config({
baseUrl: 'js',
// Paths (no extension)
paths: {
// Tests
LocationTest: 'test/model/LocationTest',
// Framework
jasmine: 'test/lib/jasmine/jasmine',
jasmineHtml: 'test/lib/jasmine/jasmine-html',
jasmineBoot: 'test/lib/jasmine/boot',
},
// Make external libraries compatible with requirejs (AMD)
shim: {
jasmineHtml: {
deps : ['jasmine']
},
jasmineBoot: {
deps : ['jasmine', 'jasmineHtml']
}
}
});
js/src/lib/require_main.js
requirejs(['./require_src_config'], function (require_src_config) {
(Application Code)
});
js/src/lib/require_test.js
requirejs(['./require_src_config'], function (require_src_config) {
requirejs(['./src/lib/require_test_config'], function (require_test_config) {
requirejs(['jasmineBoot'], function (require) {
requirejs(['LocationTest'], function (LocationTest) {
// Trigger Jasmine
window.onload();
});
});
});
});
The above all works, although I do not understand why I had to revise the path to require_config_test.js in require_test.js. The baseUrl for both configs is 'js'.
I would like to move LocationTest.html to js/test/model.
1) What should my data-main be set to?
2) How (and why) does this impact require_test.js settings?
3) Is there a better way to nest (or not) the configs for src and test to ensure src gets loaded first?
I was hoping to only have to set data-main to the path of the file with the entrypoint and be flexible to move things around. Thanks for your help!

How do I mix promises and pipe in gulp?

In my project I compile multiple bundles from source files in nested directories using rollup.
I had a gulpfile with the following code, which worked fine:
function build_app_js(file, name) {
return gulp.src(file)
.pipe(sourcemaps.init())
.pipe(rollup({format:'iife'}))
.pipe(terser())
.pipe(rename(name + '.js'))
.pipe(rename({suffix: '.min'}))
.pipe(sourcemaps.write())
.pipe(gulp.dest(js_apps_dir))
}
// call the above for multiple sets of file+app_name
But then I changed one of the dependencies in my ES6 code which I accessed by relative path into an npm package, so it is now in node_modules. Rollup needs a plugin to resolve this, so I changed the above to this:
.pipe(rollup({plugins: [resolveNodeModules()], format:'iife'}))
However this simply does not work.
I consulted rollup's docs on gulp, and adapted the example to my case, so it now looks like this:
function build_app_js(file, name) {
return rollup.rollup({
input: file,
plugins: [
resolveNodeModules()
]
}).then(bundle => {
return bundle.write({
file: js_apps_dir + '/' + name + '.js',
format: 'iife',
sourcemap: true
});
});
}
This works, but has no minification step, and I don't know how to add one.
More generally, this is a totally different paradigm from using pipe(), and I do not know how to make both work together.
Do I try to add minification in the Promise syntax, or do I wrap the Promise function in such a way that I can use it with pipe?
Answering own question after 8 days.
Minification can be achieved via rollup plugins, such as rollup-plugin-terser.
You just need to be careful with how you import them:
var rollup = require('rollup');
var resolveNodeModules = require('rollup-plugin-node-resolve');
//var terser = require('rollup-plugin-terser'); // WRONG
var {terser} = require('rollup-plugin-terser'); // CORRECT
function build_app_js(file, name) {
return rollup.rollup({
input: file,
plugins: [
resolveNodeModules(),
terser()
]
}).then(bundle => {
return bundle.write({
file: js_apps_dir + '/' + name + '.js',
format: 'iife',
sourcemap: true
});
});
}
If you import it the wrong way, you will get a terser() is not a function type error, which is because it will have imported terser as a module.
It's a bit annoying that different rollup-plugins can't be imported the same way, but hey.

sw-precache not updating and taking only cached data

Iam trying to implement networkfirst strategy using sw-precache.
Now iam able to cache the data and able to serve from offline. If i change the data (i.e: changed the header from 'hello' to 'Welcome') in page not get reflecting it always taking the data from the cache its getting update only if i unregistered the service worker or clear the site data then only i can get my data
Here is my sw-precache gulp task :
gulp.task('generate-service-worker', function(callback) {
var path = require('path');
var swPrecache = require('sw-precache');
var rootDir = '.';
swPrecache.write(path.join(rootDir, 'sw.js'), {
staticFileGlobs: [
rootDir + '/css/**.css',
rootDir + '/js/**/*.{js,css}',
rootDir + '/images/**/*.{png,jpg,jpeg}',
rootDir + '/*.{html,js}',
],
runtimeCaching: [
{
urlPattern: 'http://localhost:8080',
handler: 'networkFirst'
}],
stripPrefix: rootDir
}, callback);
});
Two things to check:
Ensure that you're not caching your sw.js file, as this could delay updates for up to 24 hours in Chrome. (Details.)
You're checking for updating content on the subsequent visit to the site following your update? Because of the cache-first strategy, the initial visit to the site following the update won't show the new content (because the cache has been updated "in the background").

gulp-cache: How can I use a file cache for LESS builds

We are using gulp to compile all our LESS files into the target/ of a Maven project. This task alone takes ~51secs, so we would like to speed it up and skip unchanged LESS files. We need a file cache because gulp is called from Maven and the build runs inside an IDE, so the gulp process cannot stay in memory.
At best, the cached CSS files should be copied to target/ even if /target was deleted by a Clean & Build.
Here's my code:
var cache = require('gulp-cache');
var fileCache = new cache.Cache({ cacheDirName: 'gulp-cache' });
gulp.task('less', function () {
return gulp.src([webappPath + '/**/*.less'])
.pipe(fileCache('less'))
.pipe(sourcemaps.init())
.pipe(less({
paths: [path.join(__dirname, 'less', 'includes')],
plugins: [cleancss],
relativeUrls: true
}))
.pipe(sourcemaps.write('.'))
.pipe(gulp.dest(target));
;
});
The line .pipe(fileCache('less')) runs into an error:
TypeError: fileCache is not a function.
(Documentation at https://www.npmjs.com/package/gulp-cache )
(1) The gulp-cache plugin needs to wrap your less plugin. That way only files that have changed will be passed through to less.
(2) You don't necessarily need to instantiate your own cache.Cache object. gulp-cache will create one for you if you don't. You only need to do it yourself if you want to have multiple caches. In that case you can pass the cache.Cache object using the fileCache option.
gulp.task('less', function () {
return gulp.src([webappPath + '/**/*.less'])
.pipe(sourcemaps.init())
.pipe(cache(less({
paths: [path.join(__dirname, 'less', 'includes')],
plugins: [cleancss],
relativeUrls: true
}), {
fileCache: new cache.Cache({ cacheDirName: 'gulp-cache' })
}))
.pipe(sourcemaps.write('.'))
.pipe(gulp.dest(target));
});

Dojo Build with NLS - requireLocalization(..) fail?

My question, while at first somewhat similar to this one, seems to be a more basic question - and might be signaling a bug in the build system. I've created a custom build for my dojo application. I only build one layer right now, here's what the profile script/object looks like:
dependencies = {
stripConsole: "all",
action: "release",
optimize: "shrinksafe",
releaseName: "myProject",
// list of locales we want to expose
localeList: "en-gb,en-us,de-de",
layers: [
{
// Name: a relative path from the dojo.js in the desination directory.
name: "../../myProject.js",
dependencies: [
"myPackage.MyDataStore",
// MyWidget depends on a few other widgets, and has its own
// translation files.
"myPackage.MyWidget"
]
}
],
prefixes: [
// These paths are relative to the location of dojo.js
[ "dijit", "../dijit" ],
[ "dojox", "../dojox" ],
[ "myPackage", "../../../src/myPackage" ]
]
}
When I run a build with that description it outputs files in the following directory structure:
release/
release/myProject/
release/myProject/dijit/
... dijit ...
release/myProject/dojo/
... dojo ...
release/myProject/dojox/
... dojox ...
release/myProject/myPackage/
... my custom package ...
release/nls/
myProject_en-us.js
myProject_de.js
etc..
../myproject.js
../myProject.js.uncompressed.js
Finally, in my test HTML page - I've got the following:
<script type="text/javascript">
var djConfig = {
debug: true,
parseOnLoad: false,
modulePaths: { // paths to directories in relation to dojo's location.... hurr.
'myPackage': '../myPackage',
'dojox': '../dojox',
'dijit': '../dijit'
}
};
</script>
<script type="text/javascript" src="./release/myProject/dojo/dojo.js.uncompressed.js"></script>
<script type="text/javascript" src="./release/myProject.js.uncompressed.js"></script>
<script type="text/javascript">
dojo.addOnLoad(function(){
dojo.require('myPackage.MyDataStore');
dojo.require('myPackage.MyWidget');
var store = new myPackage.MyDataStore();
var widget = new myPackage.MyWidget({
store: store
}, dojo.byId('testWidget'));
widget.startup();
});
</script>
But unfortunately, Firebug spits this out at me:
Bundle not found: MyWidget in myPackage , locale=en-us
What I Think is Happening
I've traced through some of the code leading up to the above error and it seems like the dojo.i18n._preloadLocalizations() call at the end of the file doesn't actually load in the correct nls file from ./release/nls.
Any idea how to fix this without resorting to manually including the nls files with <script> tags?
It's a bug of dojo, you should not use '..' in your layers name in case it will generate a NLS package.
please refer to http://bugs.dojotoolkit.org/ticket/5225

Resources