How to Invalidate Dojo/Require's Module Cache? - javascript

I have a dojo-based module that automates loading a dojo/store/Memory for me, using a bang. It's called like so:
require( [ 'squad_builder/storeBroker!collectionStore' ],
function( collectionStore )
{
// use store...
} );
The first require() call loads the store and subsequent ones load the store from a cache.
This works well but I cannot figure out how to invalidate require's internal cache so I can make the next request load the store with fresh data.
Is there anything built into require() to do this or do I need to mess with some internal workings (and if so, where?)
Edit
In the docs it mentions:
Note: the dojo loader includes the nonstandard function require.undef,
which undefines a module value.
But that method is undefined when I call it. Looking at require() in Firebug with:
for( i in require )
{
console.log( 'require', i, require[ i ] );
}
require.module has a def() method but not an undef() one.
Edit 2
Require.cache only seems to contain dojo and dijit modules. Where are custom namespaced modules cached?

According to the dojo site, the function is not automatically included in "built" dojo files. If you look at an uncompressed dojo.js you will see it there.
From:http://dojotoolkit.org/reference-guide/1.9/loader/amd.html
The following table provides a list of options that are leveraged
within the loader. The first column is the Option/Feature as defined
within the loader, the second options is whether this is a detected
feature (via has.add()) or if it is just an option and its default
value. With “unbuilt” source, all the features and options are
available. If the loader has been built, then some of these features
may have been set as staticHasFeatures and not be configurable
anymore.
Also here it says that you might be able to turn the function back on by adding a line go djconfig
http://dojo-toolkit.33424.n3.nabble.com/require-undef-td3990559.html
dojoConfig = { has : {
"dojo-undef-api": true } }

Related

How to edit an object within a very simple JS file using Node.js

Whilst this question is related to Workbox and Webpack, it does not require any prior knowledge of either library.
Background (skip if not familiar with Workbox)
I am currently utilising the InjectManifest plugin from Workbox 4.3.1 (workbox-webpack-plugin). This version of the library offers an option called manifestTransforms, but unfortunately, the transformations are not applied to assets within the webpack compilation (this is a known issue).
Whilst this has been fixed in Workbox v5+, I am unable to upgrade due to another library in my build process requiring webpack v3 (Dynamic Importing in Laravel Mix)
The reason I mention the above is because unforunately the solution is not to upgrade to workbox v5+.
The Problem
I have an auto-generated file that looks like this:
self.__precacheManifest = (self.__precacheManifest || []).concat([
{
"revision": "68cd3870a6400d76a16c",
"url": "//css/app.css"
},
// etc...
]);
I need to somehow extract the the contents of the object stored within self.__precacheManifest, apply my own transformations, and then save it back to the file.
What I have Tried...
This is as far as I have got:
// As the precached filename is hashed, we need to read the
// directory in order to find the filename. Assuming there
// are no other files called `precache-manifest`, we can assume
// it is the first value in the filtered array. There is no
// need to test if [0] has a value because if it doesn't
// this needs to throw an error
let manifest = fs
.readdirSync(path.normalize(`${__dirname}/dist/js`))
.filter(filename => filename.startsWith('precache-manifest'))[0];
require('./dist/js/' + manifest);
// This does not fire because of thrown error...
console.log(self.__precacheManifest);
This throws the following error:
self is not defined
I understand why it is throwing the error, but I have no idea how I am going to get around this because I need to somehow read the contents of the file in order to extract the object. Can anyone advise me here?
Bear in mind that once I have applied the transformations to the object, I then need to save the updated object to the file...
Since self refers to window and window does not exist in node.js a way around is needed.
One thing that should work is to define the variable self in Node's global scope and let the require statement populate the content of the variable, like this:
global['self'] = {};
require('./dist/js/' + manifest);
console.log(self.__precacheManifest);
To save the modified contents back to the file
const newPrecacheManifest = JSON.stringify(updatedArray);
fs.writeFileSync('./dist/js/' + manifest, `self.__precacheManifest = (self.__precacheManifest || []).concat(${newPrecachedManifes});`, 'utf8');

Using gulp-modernizr how do I specify feature options

My custom Moderniser build includes the following options which I am trying to inject into the gulp-modernizr tool as follows:
gulp.task('modernizr', function() {
allUISrcPlusSassSrc
.pipe(modernizr({
options: [
'backdropfilter',
'backgroundsize',
'bgpositionxy',
'bgrepeatspace_bgrepeatround',
'bgsizecover',
'borderradius',
'cssanimations',
'cssfilters',
'csspointerevents',
'csspseudoanimations',
'csspseudotransitions',
'cssremunit',
'csstransforms',
'csstransitions',
'customevent',
'devicemotion_deviceorientation',
'eventlistener',
'flexbox',
'fontface',
'json',
'nthchild',
'opacity',
'overflowscrolling',
'rgba',
'svg',
'svgasimg',
'svgfilters',
'touchevents',
'xhrresponsetypejson',
'domprefixes',
'setclasses',
'shiv'
]
}))
.pipe(gulp.dest("./output/js/vendor/"))
});
But running this leads to Building your customized Modernizr{ [Error: Error: ENOENT: no such file or directory, open '/Users/davesag/src/test-project/node_modules/modernizr/lib/../src/backgroundsize.js'
Looking in ./node_modules/modernizr/src I can't see any files that match these options. Am I just misunderstanding something? These feature names came straight from my custom-built modernizr file as downloaded previously from the moderniser website includes the following options which I am trying to inject into the gulp-modernizr.
Leaving out the options it builds okay but without the options I'm not getting any feature-classes injected into the HTML element of my page. The supposed auto-detect stuff does not seem to be working.
I checked the Customizr page as per the docs and it seems what I am doing is right. But clearly that's not the case.
How should I be doing this?
options refers to the options that are on the left hand side of the Modernizr builder page. These are typically APIs and convenience functions like Modernizr.mq, Modernizr.atRule, etc.
You want to use the tests option
#Patrick "it doesn't by default. you need to add options: ["setclasses"]"
Actually it's options: ["setClasses"]

Node.js reload external file every n-minutes

I've got a question regarding loading external files in node.
Basically I'm loading an external JSON file that contains some configuration, and this file is modified by an external process every 10 minutes. How can i reload this file every 10 minutes, without restarting node?
I've tried this solution:
delete require.cache['/home/conf/myfile.json']
but some people have advised against it. Can anybody help me?
At the end I changed my code and I now use fs.readFile to load my json file, instead of using a require.
I then used node-watch to check for changes in file and reload it.
Couple options. You could just use setInterval for 10 minutes and read the file in the callback. Otherwise you could use fs.watch and trigger the reload when the file actually changes.
update based on comment
If you're using require, it will only load the file once, regardless of how many times you're requiring it and there isn't really a mechanism to invalidate a required file. You could create a wrapper around the functionality and require that instead. Your wrapper is the file you require, and it exposes a function which returns your current config. Inside that module you could create the setTimeout refresh mentioned above.
You can have a look at my module-invalidate module that allows you to invalidate a required module. The module will then be automatically reloaded on further access.
Example:
module ./myModule.js
module.invalidable = true;
var count = 0;
exports.count = function() {
return count++;
}
main module ./index.js
require('module-invalidate');
var myModule = require('./myModule.js');
console.log( myModule.count() ); // 0
console.log( myModule.count() ); // 1
setInterval(function() {
module.constructor.invalidateByExports(myModule);
console.log( myModule.count() ); // 0
console.log( myModule.count() ); // 1
}, 10*60*1000);

how should I write my define to work with curl.js?

I'm reading Addy Osmani's excellent blog post about writing AMD modules. I start with a simple chunk of js that I lifted from his post:
define('modTest', [],
// module definition function
function () {
// return a value that defines the module export
// (i.e the functionality we want to expose for consumption)
// create your module here
var myModule = {
doStuff:function(){
console.log('Yay! Stuff');
}
}
return myModule;
}
);
I took out the dependencies on foo and bar. Just want a simple object that logs to the console.
So I save that in /js/modTest.js and then try to load it:
curl(['/js/modTest.js'])
.then(function(mt) {
console.log("Load complete");
console.log("mt:");
console.log(mt);
mt.doStuff()
}, function(ex) {alert(ex.message);})
Result: error: Multiple anonymous defines in URL. OK that didn't work. Tried adding in a namespace: define('myCompany/modTest', [],, same result. Tried adding an empty string in the dependency array, same result.
Also tried curl(['modTest.js'], function(dep){console.log(dep)}); with the same result.
Is the code in Addy's blog post incorrect? Am I doing something wrong? Maybe a bug in curl?
Update 5/24: I ditched curl.js in favor of require.js. Zero odd errors, very little work to change over. I did have to deal with amdefine a bit to get my code running client and server side (one object is in both places, so grunt had to be configured to take care of that). My defines generally look like:
define(->
class AlphaBravo
...
And never have any trouble loading.
You asked curl() to fetch a module called "/js/modTest.js". It found the file and loaded it and found a module named "modTest", so it complained. :) (That error message is horribly wrong, though!)
Here's how you can fix it (pick one):
1) Remove the ID from your define(). The ID is not recommended. It's typically only used by AMD build tools and when declaring modules inside test harnesses.
2) Refer to the module by the ID you gave it in the define(). (Again, the ID is not recommended in most cases.)
curl(['modTest'], doSomething);
3) Map a package (or a path) to the folder with your application's modules. It's not clear to me what that would be from your example since modTest appears to be a stand-alone module. However, if you were to decide to organize your app's files under an "app" package, you packages config might look like this:
packages: [ { name: 'app', location: 'app' } ]
Then, when you have code that relies on the modTest module, you can get to it via an ID of "app/modTest".
curl(['app/modTest'], doSomething);
I hope that helps clear things up!
Fwiw, Addy's example could actually work with the right configuration, but I don't see any configuration in that post (or my eyes missed it). Something like this might work:
packages: [ { name: 'app', location: '.' } ]
-- John
I've just had a similar problem which turned out to be the include order I was using for my other libraries. I'm loading handlebars.js, crossroads.js, jquery and a few other libraries into my project in the traditional way (script tags in head) and found that when I place the curl.js include first, I get this error, but when I include it last, I do not get this error.
My head tag now looks like this:
<script type="text/javascript" src="/js/lib/jquery.js"></script>
<script type="text/javascript" src="/js/lib/signals.js"></script>
<script type="text/javascript" src="/js/lib/crossroads.js"></script>
<script type="text/javascript" src="/js/lib/handlebars.js"></script>
<script type="text/javascript" src="/js/lib/curl.js"></script>
<script type="text/javascript" src="/js/main.js"></script>
You have a problem with your define call. It is NAMED
See AMD spec for full story on how to write defines, but here is what I would expect to see in your js/modTest.js file:
define(/* this is where the difference is */ function () {
// return a value that defines the module export
// (i.e the functionality we want to expose for consumption)
// create your module here
var myModule = {
doStuff:function(){
console.log('Yay! Stuff');
}
}
return myModule;
}
);
Now, the boring explanation:
CurlJS is awesome. In fact, after dealing with both, RequireJS and CurlJS, I would say CurlJS is awesome-er than RequireJS in one category - reliability of script execution ordering. So you are on the right track.
On of the major things that are different about CurlJS is that it uses "find at least one anonymous define per loaded module, else it's error" logic. RequireJS uses a timeout, where it effectively ignores cases where nothing was defined in a given file, but blows up on caught loading / parsing errors.
That difference is what is getting you here. CurlJS expects at least one anonymous (as in NOT-named) define per loaded module. It still handles named defines fine, as expected. The second you move the contents of "js/modTest.js" into inline code, you will have to "name" the define. But, that's another story.

How to isolate different javascript libraries on the same page?

Suppose we need to embed a widget in third party page. This widget might use jquery for instance so widget carries a jquery library with itself.
Suppose third party page also uses jquery but a different version.
How to prevent clash between them when embedding widgets? jquery.noConflict is not an option because it's required to call this method for the first jquery library which is loaded in the page and this means that third party website should call it. The idea is that third party site should not amend or do anything aside putting tag with a src to the widget in order to use it.
Also this is not the problem with jquery in particular - google closure library (even compiled) might be taken as an example.
What solutions are exist to isolate different javascript libraries aside from obvious iframe?
Maybe loading javascript as string and then eval (by using Function('code to eval'), not the eval('code to eval')) it in anonymous function might do the trick?
Actually, I think jQuery.noConflict is precisely what you want to use. If I understand its implementation correctly, your code should look like this:
(function () {
var my$;
// your copy of the minified jQuery source
my$ = jQuery.noConflict(true);
// your widget code, which should use my$ instead of $
}());
The call to noConflict will restore the global jQuery and $ objects to their former values.
Function(...) makes an eval inside your function, it isn't any better.
Why not use the iframe they provide a default sandboxing for third party content.
And for friendly ones you can share text data, between them and your page, using parent.postMessage for modern browser or the window.name hack for the olders.
I built a library to solve this very problem. I am not sure if it will help you of course, because the code still has to be aware of the problem and use the library in the first place, so it will help only if you are able to change your code to use the library.
The library in question is called Packages JS and can be downloaded and used for free as it is Open Source under a Creative Commons license.
It basically works by packaging code inside functions. From those functions you export those objects you want to expose to other packages. In the consumer packages you import these objects into your local namespace. It doesn't matter if someone else or indeed even you yourself use the same name multiple times because you can resolve the ambiguity.
Here is an example:
(file example/greeting.js)
Package("example.greeting", function() {
// Create a function hello...
function hello() {
return "Hello world!";
};
// ...then export it for use by other packages
Export(hello);
// You need to supply a name for anonymous functions...
Export("goodbye", function() {
return "Goodbye cruel world!";
});
});
(file example/ambiguity.js)
Package("example.ambiguity", function() {
// functions hello and goodbye are also in example.greeting, making it ambiguous which
// one is intended when using the unqualified name.
function hello() {
return "Hello ambiguity!";
};
function goodbye() {
return "Goodbye ambiguity!";
};
// export for use by other packages
Export(hello);
Export(goodbye);
});
(file example/ambiguitytest.js)
Package("example.ambiguitytest", ["example.ambiguity", "example.greeting"], function(hello, log) {
// Which hello did we get? The one from example.ambiguity or from example.greeting?
log().info(hello());
// We will get the first one found, so the one from example.ambiguity in this case.
// Use fully qualified names to resolve any ambiguities.
var goodbye1 = Import("example.greeting.goodbye");
var goodbye2 = Import("example.ambiguity.goodbye");
log().info(goodbye1());
log().info(goodbye2());
});
example/ambiguitytest.js uses two libraries that both export a function goodbye, but it can explicitly import the correct ones and assign them to local aliases to disambiguate between them.
To use jQuery in this way would mean 'packaging' jQuery by wrapping it's code in a call to Package and Exporting the objects that it now exposes to the global scope. It means changing the library a bit which may not be what you want but alas there is no way around that that I can see without resorting to iframes.
I am planning on including 'packaged' versions of popular libraries along in the download and jQuery is definitely on the list, but at the moment I only have a packaged version of Sizzle, jQuery's selector engine.
Instead of looking for methods like no conflict, you can very well call full URL of the Google API on jQuery so that it can work in the application.
<script src="myjquery.min.js"></script>
<script>window.myjQuery = window.jQuery.noConflict();</script>
...
<script src='...'></script> //another widget using an old versioned jquery
<script>
(function($){
//...
//now you can access your own jquery here, without conflict
})(window.myjQuery);
delete window.myjQuery;
</script>
Most important points:
call jQuery.noConflict() method IMMEDIATELY AFTER your own jquery and related plugins tags
store the result jquery to a global variable, with a name that has little chance to conflict or confuse
load your widget using the old versioned jquery;
followed up is your logic codes. using a closure to obtain a private $ for convience. The private $ will not conflict with other jquerys.
You'd better not forget to delete the global temp var.

Categories

Resources