Is it possible to unrequire a module in Javascript? - javascript

I am using Browserify to require a JS file, like this require('./typescript/build/crossing-puzzle-setup' . However, considering there will be many different types of puzzles, I was wondering, is it possible to "unrequire" a module if I no longer need it?

It makes little sense to unload a module, as the code only takes a very small amount of RAM, and no it is not possible to unload it as the engine cannot predict that require("some module") doesnt get called again, so it cannot unload the stored data of that module.

If you really are loading so much code that you need to force the browser to unload it, you could force the page to refresh and then load the previous state of the page. However, the user will see the browser’s “page loading” indicators during the refresh.
function startReload() {
// Save all the state you need into a string. An example object:
const importantAppState = {currentPuzzle: 'level1', username: 'roy'};
localStorage.setItem('stateBeforeReload', JSON.stringify(importantAppState));
window.location.reload();
}
// Run this function whenever your page loads:
function onLoad() {
const previousState = localStorage.setItem('stateBeforeReload');
if (previousState !== null) {
// you should re-initialize your app from the state here,
// dynamically calling `require` on only the files you need
localStorage.removeItem('stateBeforeReload');
}
}

for those looking for a nodeJS solution:
require('../myLib');
delete require.cache[require.resolve('../myLib')];
Not sure if the above clear 100% memory linked to the module. For that, a secure way seem to be using this node module: node-disrequire

Related

Angular2 with Typescript, how to use Plupload CDN script file in lazy-loaded module?

I would like to use Plupload in an Angular2 component and access the Plupload JavaScript file from a CDN. I want it specific to a component so that it is not downloaded if it is not required - I want it to be in a lazy loaded module. How can I do this?
Now fully answered on this page!
The result of this quest, which included offering and awarding bounties to two people who worked hard with me on it, is as follows:
Example of using Plupload with Angular 2 and TypeScript
How to Lazy load a script from a CDN in Angular 2
Example of how to use Plupload in a lazy loaded module
How to use a lazy loaded script in Angular 2
(See edit history for the ugly details that used to make up this question.)
Here's the overview of what you need to do to create a lazy-loaded Plupload feature while loading Plupload from a CDN:
When the feature is needed (e.g. user clicks a button or visits a page), dynamically add a <script> tag to the page to load the Plupload library from a CDN.
Wait until the library is loaded to proceed (or you could get a "plupload is undefined" error).
Display the UI to interact with Plupload in one of your Angular templates. In its simplest form, this UI consists of two buttons: "Select files" and "Upload files".
Initialize Plupload and wire it up to the UI.
Complete, working code: https://plnkr.co/edit/4t39Rod4YNAOrHmZdxqc?p=preview
Please take note of the following points in my implementation:
Regarding #2. A better way to check whether Plupload has finished loading would be to poll the global namespace for the existence of the plupload variable. As long as window.plupload does not exist, it means the library hasn't been loaded yet and that we should NOT proceed. For simplicity my code just waits for one second and proceeds.
Number 4 can prove a bit tricky. Plupload makes a heavy use of direct DOM access to wire its API to the HTML (e.g. document.getElementById('filelist')). This is something Angular discourages and that you should try avoiding whenever possible. More specifically direct DOM access is used in the following places:
To tell Plupload which DOM element should trigger the "Select files" dialog (what they call the browse_button config option). For this I could not avoid the direct DOM reference and I used the #ViewChild decorator to get a hold of the "Select Files" button.
To display selected files in the template. For this I converted the Plupload syntax into the regular Angular syntax. I push selected files to a class property called fileList which I display in the template using a standard *ngFor.
The "Upload Files" button triggers some code that does the actual uploading and refreshes the UI to show upload progress. Once more, I converted this to regular Angular syntax using event binding and data binding.
Let me know if you have any questions.
In this approach no need for any extra loader modules.
See example (check console for Woohoo): http://plnkr.co/edit/gfUs4Uhe8kMGzPxwpBay?p=preview
updated plunker: https://plnkr.co/edit/leG062tg7uX8sLrA0i2i?p=preview
You can lazyload some js by adding the script url to you document:
Create a my-lazy-load.function.ts:
export function lazyload(url) {
// based on https://friendlybit.com/js/lazy-loading-asyncronous-javascript/
let scripts = document.getElementsByTagName('script');
for (let i = scripts.length; i--;) {
if (scripts[i].src.match(url)) return true;
}
let s = document.createElement('script');
s.type = 'text/javascript';
s.async = true;
s.src = url;
let x = document.getElementsByTagName('script')[0];
x.parentNode.insertBefore(s, x);
return true;
}
In your component that you want to add plupload:
import {lazyload} from "./my-lazy-load.function.ts";
export class MyComponent implements OnInit {
pluploadInterval:number = null;
hasPlupload: boolean = false;
ngOnInit() {
lazyload("https://cdnjs.cloudflare.com/ajax/libs/plupload/2.3.1/plupload.full.min.js");
this.pluploadInterval = window.setInterval(()=>{
if(window.plupload) { // you can check existence of plupload object any way you want
// Woohoo, I am ready to be used
this.hasPlupload = true; // everything is run outside ngZone, wrap it if angular2 is not reacting to changes, or change window.setInterval to setInterval
window.clearInterval(this.pluploadInterval); // don't forget to clean interval
}
}, 100); // timeinterval can vary
....
The browser will load this automatically.
Notice if(plupload) it assumes that there is global object plupload that the script adds (I do not know if it truely added, check your working example in pure javascript). As it is jquery extension you can check it's prototype like this: jQuery test for whether an object has a method?
OLD HISTORICAL:
#Reid here is plunker: https://plnkr.co/edit/zDWWQbTQUSHBqCsrUMUi?p=preview the plupload is actually loaded, but added to require with define("plupload", ['./moxie'], extract); I am not sure at the moment how to extract from there and which package require is belong to... the code for finding correct module loader belongs to plupload itself, here it is (from plupload.dev.js):
if (typeof define === "function" && define.amd) {
define("plupload", ['./moxie'], extract);
} else if (typeof module === "object" && module.exports) {
module.exports = extract(require('./moxie'));
} else {
global.plupload = extract(global.moxie);
}
I think that your best bet is to use the Require.js Library so that you can dynamically load your scripts from within your components.
The small trade off is that you will have to add this 18KB library to your index.html page (CDN), however this could save you huge amounts of loading if your 3rd party libraries are massive.
I have no experience with using plupload, so instead I put together the following plunkr which uses an external animation library, drawn from a CDN. The plunkr animates a number from 0 - 100.
https://plnkr.co/edit/fJCtezsERYHOYplLh7Jo?p=preview
index.html
<script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.3/require.min.js"></script>
component.ts
ngOnInit(){
// Dynamically loads the framework from the CDN.
require(["https://cdnjs.cloudflare.com/ajax/libs/gsap/1.19.1/TweenLite.min.js"],
// This function is then called ONCE LOAD IS COMPLETE
function (common) {
// Do greensock things
var demo = {score:0},
scoreDisplay = document.getElementById("scoreDisplay");
//create a tween that changes the value of the score property of the demo object from 0 to 100 over the course of 20 seconds.
var tween = TweenLite.to(demo, 20, {score:100, onUpdate:showScore})
//each time the tween updates this function will be called.
function showScore() {
scoreDisplay.innerHTML = demo.score.toFixed(2);
}
});
}
What I like about this approach, is that in the onLoad callback from require, the syntax is unchanged from a normal implementation of the library, so you can just copy paste your currently working code into the callback.

Rendering React.js clientside webapp with PhantomJS

A friend has asked me to capture a client-side rendered website built with React.js, preferably using PhantomJS. I'm using a simple rendering script as follows:
var system = require('system'),
fs = require('fs'),
page = new WebPage(),
url = system.args[1],
output = system.args[2],
result;
page.open(url, function (status) {
if (status !== 'success') {
console.log('FAILED to load the url');
phantom.exit();
} else {
result = page.evaluate(function(){
var html, doc;
html = document.querySelector('html');
return html.outerHTML;
});
if(output){
var rendered = fs.open(output,'w');
rendered.write(result);
rendered.flush();
rendered.close();
}else{
console.log(result);
}
}
phantom.exit();
});
The url is http://azertyjobs.tk
I consistently get an error
ReferenceError: Can't find variable: Promise
http://azertyjobs.tk/build/bundle.js:34
http://azertyjobs.tk/build/bundle.js:1 in t
...
Ok so I figured out that ES6 Promises aren't natively supported by PhantomJS yet, so I tried various extra packages like the following https://www.npmjs.com/package/es6-promise and initiated the variable as such:
var Promise = require('es6-promise').Promise
However this still produces the same error, although Promise is now a function. The output of the webpage is also still as good as empty (obviously..)
Now I'm pretty oldschool, so this whole client-side rendering stuff is kind of beyond me (in every aspect), but maybe someone has a solution. I've tried using a waiting script too, but that brought absolutely nothing. Am I going about this completely wrong? Is this even possible to do?
Much appreciated!
Ludwig
I've tried the polyfill you linked and it didn't work, changed for core.js and was able to make a screenshot. You need to inject the polyfill before the page is opened:
page.onInitialized = function() {
if(page.injectJs('core.js')){
console.log("Polyfill loaded");
}
}
page.open(url, function (status) {
setTimeout(function(){
page.render('output.jpg');
phantom.exit();
}, 3000);
});
What you need to understand is that there are several parts of a page loading. First there is the HTML - the same thing you see when you "view source" on a web page. Next there are images and scripts and other resources loaded. Then the scripts are executed, which may or may not result in more content being loaded and possible modifications to the HTML.
What you must do then is figure out a way to determine when the page is actually "loaded" as the user sees it. PhantomJS provides a paradigm for you to waitFor content to load. Read through their example and see if you can figure out a method which works for you. Take special note of where they put phantom.exit(); as you want to make sure that happens at the very end. Good luck.
Where (how) are you trying to initialise Promise? You'll need to create it as a property of window, or use es6-promise as a global polyfill, like this require('es6-promise').polyfill(); or this require('es6-promise/auto'); (from the readme).
Also, what do you mean by "capture"? How If you're trying to scrape data, you may have better luck using X-ray. It supports Phantom, Nightmare and other drivers.
Keep in mind also that React can also be server rendered. React is like templating, but with live data bindings. It's not as complicated as you're making it out to be.

Is there a node way to detect if a network interface comes online?

I have looked at the os module and the ip module but those are really good at telling me the current ip address of the system not if a new one comes online or goes offline. I know I can accomplish this problem using udev rules (I'm on Ubuntu) but I was hoping for a way to do this using only node. How would I go about discovering if a network interface is started?
You could always setup a listener using process.nextTick and see if the set of interfaces has changed since last time. If so, send out the updates to any listeners.
'use strict';
let os = require('os');
// Track the listeners and provide a way of adding a new one
// You would probably want to put this into some kind of module
// so it can be used in various places
let listeners = [];
function onChange(f) {
if (listeners.indexOf(f) === -1) {
listeners.push(f);
}
}
let oldInterfaces = [];
process.nextTick(function checkInterfaces() {
let interfaces = os.networkInterfaces();
// Quick and dirty way of checking for differences
// Not very efficient
if (JSON.stringify(interfaces) !== JSON.stringify(oldInterfaces)) {
listeners.forEach((f) => f(interfaces));
oldInterfaces = interfaces;
}
// Continue to check again on the next tick
process.nextTick(checkInterfaces);
});
// Print out the current interfaces whenever there's a change
onChange(console.log.bind(console));
Unfortunately, there is no event bases way to do this in node. The solution we came up with was to use UDEV to generate events when a device goes offline and comes online.
In short, you can use npm package network-interfaces-listener. It will send data every time an(or all) interface becomes online, or offline. Not a perfect solution, but it will do.
I tried the answer of Mike Cluck. The problem I faced with nextTick() approach is that it gets called in the end.
The package creates a separate thread(or worker in nodejs). The worker has setInterval() which calls the passed callback function after every second. The callback compares previous second data, if it does not match, then change has happened, and it calls listener.
Note(edit): The package mentioned is created by me in order to solve the problem.

NodeJS, SocketIO and Express logic context build

I read a lot about Express / SocketIO and that's crazy how rarely you get some other example than a "Hello" transmitted directly from the app.js. The problem is it doesn't work like that in the real world ... I'm actually desperate on a logic problem which seems far away from what the web give me, that's why I wanted to point this out, I'm sure asking will be the solution ! :)
I'm refactoring my app (because there were many mistakes like using the global scope to put libs, etc.) ; Let's say I've got a huge system based on SocketIO and NodeJS. There's a loader in the app.js which starts the socket system.
When someone join the app it require() another module : it initializes many socket.on() which are loaded dynamically and go to some /*_socket.js files in a folder. Each function in those modules represent a socket listener, then it's way easier to call it from the front-end, might look like this :
// Will call `user_socket.js` and method `try_to_signin(some params)`
Queries.emit_socket('user.try_to_signin', {some params});
The system itself works really well. But there's a catch : the module that will load all those files which understand what the front-end has sent also transmit libraries linked with req/res (sessions, cookies, others...) and must do it, because the called methods are the core of the app and very often need those libraries.
In the previous example we obviously need to check if the user isn't already logged-in.
// The *_socket.js file looks like this :
var $h = require(__ROOT__ + '/api/helpers');
module.exports = function($s, $w) {
var user_process = require(__ROOT__ + '/api/processes/user_process')($s, $w);
return {
my_method_called: function(reference, params, callback) {
// Stuff using $s, $w, etc.
}
}
// And it's called this way :
// $s = services (a big object)
// $w = workers (a big object depending on $s)
// They are linked with the req/res from the page when they are instantiated
controller_instance = require('../sockets/'+ controller_name +'_socket')($s, $w);
// After some processes ...
socket_io.on(socket_listener, function (datas, callback) {
// Will call the correct function, etc.
$w.queries.handle_socket($w, controller_name, method_name, datas);
});
The good news : basically, it works.
The bad news : every time I refresh the page, the listeners double themselves because they are in a loop called on page load.
Below, this should have been one line :
So I should put all the socket.on('connection'...) stuff outside the page loading, which means when the server starts ... Yes, but I also need the req/res datas to be able to load the libraries, which I get only when the page is loaded !
It's a programing logic problem, I know I did something wrong but I don't know where to go now, I got this big system which "basically" works but there's like a paradox on the way I did it and I can't figure out how to resolve this ... It's been a couple of hours I'm stuck.
How can I refacto to let the possibility to get the current libraries depending on req/res within a socket.on() call ? Is there a trick ? Should I think about changing completely the way I did it ?
Also, is there another way to do what I want to do ?
Thank you everyone !
NOTE : If I didn't explain well or if you want more code, just tell me :)
EDIT - SOLUTION : As seen above we can use sockets.once(); instead of sockets.on(), or there's also the sockets.removeAllListeners() solution which is less clean.
Try As Below.
io.sockets.once('connection', function(socket) {
io.sockets.emit('new-data', {
channel: 'stdout',
value: data
});
});
Use once instead of on.
This problem is similar as given in the following link.
https://stackoverflow.com/questions/25601064/multiple-socket-io-connections-on-page-refresh/25601075#25601075

Node.js changing exports on the fly

changing exports.X in a function seems to not work...
I want to be able to load settings from a file & access them in Node.js. I have this currently, however, the clients connecting to my node application can edit what's in the settings file. Unfortunately as it stands the Node application has to be restarted for the changes to take effect. Is there a way I can reload the module.exports on the fly?
EDIT:
Settings file is literally a JSON string.
My settings module is 'required' in almost every single file, and there's a lot of files... So reloading it per-file basis is out of the question. I do, however, know precisely when someone makes a change to the settings.
If you are using require to load the settings and only referencing the settings from one module, then doing something along the lines of:
delete require.cache[require.resolve(filename)];
will work for you.
If, on the other hand, multiple modules will be referencing these settings, that approach can become a bit unwieldy and open you up to unforeseen bugs. For example, if any of the modules are holding on to a reference to the required settings file, they would each need to somehow learn that the settings had changed and update their references.
To alleviate (not completely solve) the caching issue, you build your settings interface so that users of it must access either the settings object via a function and/or require that properties are accessed via functions. Even with this model, someone may still decide to cache a setting causing an obscure failure later down the road.
Using the simplest approach of a single getter for the settings object would look something like this:
var settings = require('./settings.json');
// ... watch for changes and reload by invalidating node's cache
module.exports = function() { return settings; }
Usage:
var settings = require('./path/to/settings');
settings().foo;
There are several libraries that do settings. Depending on your needs, I'm partial to nconf.
I'd set up a file watcher here that checks for changes of a JSON file dynamically. It is not recommended practice to change a JS script once the app is running.
Something like:
var _ = require("lodash");
var fs = require("fs");
var result = {};
fs.watch('my-settings.json',function(event,filename){
fs.readFile(filename,function(err,data){
if(err){
// your error catching
}
_.extend(result,JSON.parse(data));
});
});
module.exports = result;
Now, this comes with lots of caveats, first that fs.watch is not always supported by all platforms.
http://nodejs.org/api/fs.html#fs_fs_watch_filename_options_listener
Second, that it's really awkward to change a property like this. The expectation is generally that exports of module not mutate. I'd instead recommend exposing a method whose result can change based on the state of the file, a getter for the resulting data.
Third, a file watcher can be expensive, memory-wise.
This is better code, IMHO:
var _ = require("lodash");
var fs = require("fs");
var filename = 'my-settings.json';
var lastModified;
var mySetting;
module.exports = {
getSettingAsync : function (callback) {
fs.stat(filename,function(err,stat){
if(stat.mtime == lastModified) {
callback(mySetting);
} else {
fs.readFile(filename,function(err,data){
if(err){
// your error catching
}
// this assumes that your data is always correct
mySetting = JSON.parse(data).mySetting;
callback(mySetting);
});
}
});
}
};
In this case, we both check for a JSON file, and expose this as an async method. You could just as easily change the code to use the sync versions if need be and return the value instead of invoking the callback. This version checks when the file was changed, which is cheaper than reading the whole file every time, reads the file if newer and saves you the need to use a potentially buggy file watcher.
By the way, I've not tested this code and it may contain errors as is, but the concept is sound.
But, perhaps the more salient question, why not just store that value in the database?

Categories