How to avoid default errors from a library - javascript

I have created a code that it actually works, but I call a library that has an error, and I would like to know if it is possible to avoid that specific line of code. I will try to explain the case as well as possible:
Error
Uncaught TypeError: fs.openSync is not a function
Previous code
function synthesizeToAudioFile(authorizationToken, message) {
// replace with your own subscription key,
// service region (e.g., "westus"), and
// the name of the file you save the synthesized audio.
var serviceRegion = "westus"; // e.g., "westus"
var filename = "./audiocue.wav";
//Use token.Otherwise use the provided subscription key
var audioConfig, speechConfig;
audioConfig = SpeechSDK.AudioConfig.fromAudioFileOutput(filename);
speechConfig = SpeechSDK.SpeechConfig.fromAuthorizationToken(authorizationToken, serviceRegion);
// create the speech synthesizer.
var synthesizer = new SpeechSDK.SpeechSynthesizer(speechConfig, audioConfig);
// start the synthesizer and wait for a result.
synthesizer.speakTextAsync(message,
function (result) {
if (result.reason === SpeechSDK.ResultReason.SynthesizingAudioCompleted) {
console.log("synthesis finished.");
} else {
console.error("Speech synthesis canceled, " + result.errorDetails +
"\nDid you update the subscription info?");
}
synthesizer.close();
synthesizer = undefined;
},
function (err) {
console.trace("err - " + err);
synthesizer.close();
synthesizer = undefined;
});
console.log("Now synthesizing to: " + filename);
}
I created a method, which later I have replicated in my current code. The difference was that I was using Browserify in order to import a library from a script of a HTML file:
<script type="text/javascript" src="js/dist/sub_mqtt.js"></script>
This file had my method, and the whole library, which made it crazy unreadable, and therefore I started using ScriptJS to import it. The problem is that, using browserify I was able to remove the line of code that it was failing using fs.openSync(and I do not even need), but by importing it with ScriptJS I do not have access to the source code.
I assume that what is missing is that I am not importing the library fs, which is being used by the library that I am importing with ScriptJS before importing that one, but how could I do it? I have tried:
<script src="../text-to-speech/node_modules/fs.realpath/index.js"></script>
, or
<script type="text/javascript" src="../text-to-speech/node_modules/fs.realpath/index.js"></script>
and also wrapping the content of synthesizeToAudioFile() with
require(["node_modules/fs.realpath/index.js"], function (fs) { });
but I get the following error:
Uncaught ReferenceError: module is not defined
at index.js:1

After researching about this question I found out the next statement:
The fs package on npm was empty and didn't do anything, however many
packages mistakenly depended on it. npm, Inc. has taken ownership of
it.
It's also a built-in Node module. If you've depended on fs, you can
safely remove it from your package dependencies.
therefore what I have done is to access to the file that I was requiring with ScriptJS
require(["../text-to-speech/microsoft.cognitiveservices.speech.sdk.bundle.js"]
and directly remove that line on it. Note that, at least in my case, clearing the cache of the browser was needed.

Related

Can I allow dynamic loading of JS modules from the client side?

In my web app I want to give users the ability to author their own modules for use in the app. These are ideally written in JS on the client side.
However, I'm having trouble allowing for classes created on the client side to be accessed by the app. Originally I wanted some kind of module import, but dynamic imports still require a path, which isn't accessible by the browser for security reasons. Just doing a straight import of the JS into a script tag pollutes the global namespace, which isn't ideal either.
Is there some sensible way to do this? I would ideally want to import the class, e.g.
// MyClass.js, on the client side
export default class MyClass {
myPrint() {
console.log('ya blew it');
}
}
And then in App.js:
import(somehow_get_this_path).then((MyClass) => { etc});
Is getting that path possible? The current namespace-polluting method uses a select file dialog but doesn't allow me to tell import what path it has. All you get is a blob. I'm pretty new to this stuff so apologies if this question is dumb.
edit: I've tried getting an object URL using CreateObjectURL, which gives the error:
Error: Cannot find module 'blob:null/d651a896-d568-437f-86d0-72ebcee7bc56'
if you are using webpack as a bundler then use can use magic comments to lazy load the components.
or else you can use dynamic imports.
import('path_to_my_class').then(MyClass => {
// Do something with MyClass
});
Edited 1:-
You can use this code to get a working local URL for the uploaded js file. try using this
const path = (window.URL || window.webkitURL).createObjectURL(file);
console.log('path', path);
Edited 2:-
Alternatively, you can create your own object at a global level and wrap the file using an anonymous function (creating closure) such that it won't pollute the global namespace.
// you can add other methods to it according to your use case.
window.MyPlugins = {
plugins: [],
registerPlugin: function (plugin){
this.plugins.push(plugin);
}
}
// MyFirstPlugin.js
// you can inject this code via creating a script tag.
(function(w){ //closure
function MyFirstPlugin() {
this.myPrint = function (){
console.log('ya blew it');
}
}
w.MyPlugins.registerPlugin(new MyFirstPlugin());
})(window)
// get the reference to the plugin in some other file
window.MyPlugins.plugins
Read file and use eval
<script>
function importJS(event) {
var input = event.target;
var reader = new FileReader();
reader.onload = function(){
eval(reader.result);
input.value='';
};
reader.readAsText(input.files[0]);
};
</script>
Select a *.js file and execute
<br>
<br>
<input type="file" onchange="importJS(event);">

How do I code event/command handlers for my Discord.js bot?

I've started creating a Discord bot in Node.js using the Discord.js library. However, all the code is contained within a single index file.
How do I organize the commands and events each into separate files, and run them when needed?
An excellent, clean way to organize the code for your bot is to employ event and command handlers.
In simple terms.
You start out with a small index file to initialize the client and the rest of the code. An event handler keeps the files for each event, and calls on them when the event is emitted. Then, in your client's message event, you can avoid long if chains and switch/case altogether by running the code from the intended command's file.
What's a module?
The basic Node.js structure you'll need to understand is a module.
[A module is a] set of functions you want to include in your application.
Quoted from w3schools.com.
So, think of a module as a neatly taped up box containing pieces of code. You can take the package somewhere, open it up, and unpack the pieces. In JavaScript terms, you can require the module somewhere else in your program, and utilize the code contained within it. Modules can contain variables, classes, functions, etc. that you need to use throughout different locations across your code.
Working with modules and exports.
Now that you know what a module is, you have to understand how to work with them.
For the purpose of the handlers, you're only going to be using the exports property of the module object. By using require() for a module, module.exports is returned. Consider the following setups.
A single export.
Question.js
class Question {
constructor(author, details) {
this.author = author;
this.details = details;
this.answers = [];
}
}
module.exports = Question;
newQuestion.js
const Question = require('./Question.js');
const myQuestion = new Question('me', 'How to code event/command handlers?');
In Question.js, a new class, Question, is created and assigned to module.exports. Then, when Question.js is required in newQuestion.js, Question is declared as the exported class. It can be used just as usual.
Multiple exports.
Now, for example, if you needed to export multiple classes...
Posts.js
class Question {...}
class Answer {...}
module.exports = { Question, Answer };
// Alternatively...
// module.exports.Question = Question;
// module.exports.Answer = Answer;
newQuestion.js
const { Question } = require('./Posts.js');
const myQuestion = new Question(...);
In this way, module.exports is defined as an object, containing the created classes. This means that require() will return an object instead, so you can destructure the needed class from the object.
Creating the event handler.
You should start by creating a folder for your events, and create a file for each one. Name the files according to the name of the event. For example, for your client's message event, the file should be named message.js.
Setting up the event files.
Implementing what you now know about modules, you can code the event files. For example...
message.js
module.exports = (client, message) => {
// This code will be executed when
// the 'message' event is emitted.
};
Setting up the handler.
To make the actual handler, you can place the following code in a function to load events...
const requireAll = require('require-all'); // Don't forget to install!
const files = requireAll({ // Require all the files within your
dirname: `${__dirname}/events`, // event directory which have a name
filter: /^(?!-)(.+)\.js$/ // ending in '.js' NOT starting
}); // with '-' (a way to disable files).
client.removeAllListeners(); // Prevent duplicate listeners on reload.
// CAUTION: THIS REMOVES LISTENERS
// ATTACHED BY DISCORD.JS!
for (const name in files) { // Iterate through the files object
const event = files[name]; // and attach listeners to each
// event, passing 'client' as the
client.on(name, event.bind(null, client)); // first parameter, and the rest
// of the expected parameters
console.log(`Event loaded: ${name}`); // afterwards. Then, log the
} // successful load to the console.
Now, when your client emits one of the events you have a file for, the code inside of it is run.
Creating the command handler.
Just like for the event handler, you should start by creating a separate folder for your commands, and create files for each individual command.
Setting up the command files.
Instead of exporting just one function, you can export a "run" function and a configuration object.
help.js
module.exports.run = async (client, message, args) => {
// This code will be executed to
// run the 'help' command.
};
module.exports.config = {
name: 'help',
aliases: ['h'] // Even if you don't want an alias, leave this as an array.
};
Setting up the handler.
Just like the event handler, place this code in a function to load commands...
const requireAll = require('require-all'); // Using the same npm module...
const files = requireAll({ // Require all the files within your
dirname: `${__dirname}/commands`, // command directory which have a name
filter: /^(?!-)(.+)\.js$/ // ending in '.js' NOT starting
}); // with '-' (a way to disable files).
client.commands = new Map(); // Create new Maps for the corresponding
client.aliases = new Map(); // command names/commands, and aliases.
for (const name in files) { // Iterate through the files object
const cmd = files[name]; // and set up the 'commands' and
// 'aliases' Maps. Then, log the
client.commands.set(cmd.config.name, cmd); // successful load to the console.
for (const a of cmd.config.aliases) client.aliases.set(a, cmd.config.name);
console.log(`Command loaded: ${cmd.config.name}`);
}
In your client's message event, you can use the following code to run the commands...
const prefix = '!'; // Example
const [cmd, ...args] = message.content.trim().slice(prefix.length).split(/\s+/g);
const command = client.commands.get(cmd) || client.commands.get(client.aliases.get(cmd));
if (command) {
command.run(client, message, args);
console.log(`Executing ${command.config.name} command for ${message.author.tag}.`);
}
FAQ.
What if I have a database related or other variable I need to pass through events/commands?
For events, you can pass your variable in event.on(...), following client. Then in your actual events, your function must include that parameter after client.
For commands, you can pass your variable into the run function when calling it in the message event. Again, in your function, you need to include the properly placed parameter.
What if I want to have commands/events within subfolders?
Check out this answer to search recursively.
How do I use these handlers for a reload command?
If you placed the code for them inside of functions, you can set up a "reload" command that calls those functions, loading the events and commands again.
Related resources.
Node.js Documentation
MDN Documentation
W3Schools Tutorial
require-all Package
Discord.js Documentation
Edits...
client.removeAllListeners() will remove all listeners attached to the client, including those originating from client instantiation. This can cause voice connection related errors, specifically Voice connection not established within 15 seconds being thrown. To prevent this issue, keep track of every listener function and remove each individually using client.removeListener(listener).

Rails 5/6: How to include JS functions with webpacker?

I am trying to update a Rails 3 app to Rails 6 and I have problems with the now default webpacker since my Javascript functions are not accessible.
I get: ReferenceError: Can't find variable: functionName for all js function triggers.
What I did is:
create an app_directory in /app/javascript
copied my development javascript file into the app_directory and renamed it to index.js
added console.log('Hello World from Webpacker'); to index.js
added import "app_directory"; to /app/javascript/packs/application.js
added to /config/initializers/content_security_policy.rb:
Rails.application.config.content_security_policy do |policy|
policy.connect_src :self, :https, "http://localhost:3035", "ws://localhost:3035" if Rails.env.development?
end
I get 'Hello World from Webpacker' logged to console, but when trying to access a simple JS function through <div id="x" onclick="functionX()"></div> in the browser I get the reference error.
I understand that the asset pipeline has been substituted by webpacker, which should be great for including modules, but how should I include simple JS functions? What am I missing?
Thanks in advance?
For instructions on moving from the old asset pipeline to the new webpacker way of doing things, you can see here:
https://www.calleerlandsson.com/replacing-sprockets-with-webpacker-for-javascript-in-rails-5-2/
This is a howto for moving from the asset pipeline to webpacker in Rails 5.2, and it gives you an idea of how things are different in Rails 6 now that webpacker is the default for javascript. In particular:
Now it’s time to move all of your application JavaScript code from
app/assets/javascripts/ to app/javascript/.
To include them in the JavaScript pack, make sure to require them in
app/javascript/pack/application.js:
require('your_js_file')
So, create a file in app/javascript/hello.js like this:
console.log("Hello from hello.js");
Then, in app/javascript/packs/application.js, add this line:
require("hello")
(note that the extension isn't needed)
Now, you can load up a page with the browser console open and see the "Hello!" message in the console. Just add whatever you need in the app/javascript directory, or better yet create subdirectories to keep your code organized.
More information:
This question is cursed. The formerly accepted answer is not just wrong but grotesquely wrong, and the most upvoted answer is still missing the mark by a country mile.
anode84 above is still trying to do things the old way, and webpacker will get in your way if you try that. You have to completely change the way you do javascript and think about javascript when you move to webpacker. There is no "scoping issue". When you put code in a web pack it's self-contained and you use import/export to share code between files. Nothing is global by default.
I get why this is frustrating. You're probably like me, and accustomed to declaring a function in a javascript file and then calling it in your HTML file. Or just throwing some javascript at the end of your HTML file. I have been doing web programming since 1994 (not a typo), so I've seen everything evolve multiple times. Javascript has evolved. You have to learn the new way of doing things.
If you want to add an action to a form or whatever, you can create a file in app/javascript that does what you want. To get data to it, you can use data attributes, hidden fields, etc. If the field doesn't exist, then the code doesn't run.
Here's an example that you might find useful. I use this for showing a popup if a form has a Google reCAPTCHA and the user hasn't checked the box at the time of form submission:
// For any form, on submit find out if there's a recaptcha
// field on the form, and if so, make sure the recaptcha
// was completed before submission.
document.addEventListener("turbolinks:load", function() {
document.querySelectorAll('form').forEach(function(form) {
form.addEventListener('submit', function(event) {
const response_field = document.getElementById('g-recaptcha-response');
// This ensures that the response field is part of the form
if (response_field && form.compareDocumentPosition(response_field) & 16) {
if (response_field.value == '') {
alert("Please verify that you are not a robot.");
event.preventDefault();
event.stopPropagation();
return false;
}
}
});
});
});
Note that this is self-contained. It does not rely on any other modules and nothing else relies on it. You simply require it in your pack(s) and it will watch all form submissions.
Here's one more example of loading a google map with a geojson overlay when the page is loaded:
document.addEventListener("turbolinks:load", function() {
document.querySelectorAll('.shuttle-route-version-map').forEach(function(map_div) {
let shuttle_route_version_id = map_div.dataset.shuttleRouteVersionId;
let geojson_field = document.querySelector(`input[type=hidden][name="geojson[${shuttle_route_version_id}]"]`);
var map = null;
let center = {lat: 36.1638726, lng: -86.7742864};
map = new google.maps.Map(map_div, {
zoom: 15.18,
center: center
});
map.data.addGeoJson(JSON.parse(geojson_field.value));
var bounds = new google.maps.LatLngBounds();
map.data.forEach(function(data_feature) {
let geom = data_feature.getGeometry();
geom.forEachLatLng(function(latlng) {
bounds.extend(latlng);
});
});
map.setCenter(bounds.getCenter());
map.fitBounds(bounds);
});
});
When the page loads, I look for divs with the class "shuttle-route-version-map". For each one that I find, the data attribute "shuttleRouteVersionId" (data-shuttle-route-version-id) contains the ID of the route. I have stored the geojson in a hidden field that can be easily queried given that ID, and I then initialize the map, add the geojson, and then set the map center and bounds based on that data. Again, it's self-contained except for the Google Maps functionality.
You can also learn how to use import/export to share code, and that's really powerful.
So, one more that shows how to use import/export. Here's a simple piece of code that sets up a "watcher" to watch your location:
var driver_position_watch_id = null;
export const watch_position = function(logging_callback) {
var last_timestamp = null;
function success(pos) {
if (pos.timestamp != last_timestamp) {
logging_callback(pos);
}
last_timestamp = pos.timestamp;
}
function error(err) {
console.log('Error: ' + err.code + ': ' + err.message);
if (err.code == 3) {
// timeout, let's try again in a second
setTimeout(start_watching, 1000);
}
}
let options = {
enableHighAccuracy: true,
timeout: 15000,
maximumAge: 14500
};
function start_watching() {
if (driver_position_watch_id) stop_watching_position();
driver_position_watch_id = navigator.geolocation.watchPosition(success, error, options);
console.log("Start watching location updates: " + driver_position_watch_id);
}
start_watching();
}
export const stop_watching_position = function() {
if (driver_position_watch_id) {
console.log("Stopped watching location updates: " + driver_position_watch_id);
navigator.geolocation.clearWatch(driver_position_watch_id);
driver_position_watch_id = null;
}
}
That exports two functions: "watch_position" and "stop_watching_position". To use it, you import those functions in another file.
import { watch_position, stop_watching_position } from 'watch_location';
document.addEventListener("turbolinks:load", function() {
let lat_input = document.getElementById('driver_location_check_latitude');
let long_input = document.getElementById('driver_location_check_longitude');
if (lat_input && long_input) {
watch_position(function(pos) {
lat_input.value = pos.coords.latitude;
long_input.value = pos.coords.longitude;
});
}
});
When the page loads, we look for fields called "driver_location_check_latitude" and "driver_location_check_longitude". If they exist, we set up a watcher with a callback, and the callback fills in those fields with the latitude and longitude when they change. This is how to share code between modules.
So, again, this is a very different way of doing things. Your code is cleaner and more predictable when modularized and organized properly.
This is the future, so fighting it (and setting "window.function_name" is fighting it) will get you nowhere.
Looking at how webpacker "packs" js files and functions:
/***/ "./app/javascript/dashboard/project.js":
/*! no static exports found */
/***/ (function(module, exports) {
function myFunction() {...}
So webpacker stores these functions within another function, making them inaccessible. Not sure why that is, or how to work around it properly.
There IS a workaround, though. You can:
1) change the function signatures from:
function myFunction() { ... }
to:
window.myFunction = function() { ... }
2) keep the function signatures as is, but you would still need to add a reference to them as shown here:
window.myFunction = myFunction
This will make your functions globally accessible from the "window" object.
Replace the code in your custom java Script file
from
function function_name() {// body //}
to
window.function_name = function() {// body //}
From the official rails app guide:
Where to Stick Your JavaScript
Whether you use the Rails asset
pipeline or add a tag directly to a view, you have to make a
choice about where to put any local JavaScript file.
We have a choice of three locations for a local JavaScript file:
The app/assets/javascripts folder,the lib/assets/javascripts folder and the vendor/assets/javascripts folder
Here are guidelines for selecting
a location for your scripts:
Use app/assets/javascripts for JavaScript you create for your
application.
Use lib/assets/javascripts for scripts that are shared by many
applications (but use a gem if you can).
Use vendor/assets/javascripts for copies of jQuery plugins, etc., from
other developers. In the simplest case, when all your JavaScript files
are in the app/assets/javascripts folder, there’s nothing more you
need to do.
Add JavaScript files anywhere else and you will need to understand how
to modify a manifest file.
More reading:
http://railsapps.github.io/rails-javascript-include-external.html

Getting name of a module in node.js

Does anyone know how to get the name of a module in node.js / javascript
so lets say you do
var RandomModule = require ("fs")
.
.
.
console.log (RandomModule.name)
// -> "fs"
If you are trying to trace your dependencies, you can try using require hooks.
Create a file called myRequireHook.js
var Module = require('module');
var originalRequire = Module.prototype.require;
Module.prototype.require = function(path) {
console.log('*** Importing lib ' + path + ' from module ' + this.filename);
return originalRequire(path);
};
This code will hook every require call and log it into your console.
Not exactly what you asked first, but maybe it helps you better.
And you need to call just once in your main .js file (the one you start with node main.js).
So in your main.js, you just do that:
require('./myRequireHook');
var fs = require('fs');
var myOtherModule = require('./myOtherModule');
It will trace require in your other modules as well.
This is the way transpilers like babel work. They hook every require call and transform your code before load.
I don't know why you would need that, but there is a way.
The module variable, which is loaded automatically in every node.js file, contains an array called children. This array contains every child module loaded by require in your current file.
This way, you need to strict compare your loaded reference with the cached object version in this array in order to discover which element of the array corresponds to your module.
Look this sample:
var pieces = require('../src/routes/headers');
var childModuleId = discoverChildModuleId(pieces, module);
console.log(childModuleId);
function discoverChildModuleId(object, moduleObj) {
"use strict";
var childModule = moduleObj.children.find(function(child) {
return object === child.exports;
});
return childModule && childModule.id;
}
This code will find the correspondence in children object and bring its id.
I put module as a parameter of my function so you can export it to a file. Otherwise, it would show you modules of where discoverChildModule function resides (if it is in the same file won't make any difference, but if exported it will).
Notes:
Module ids have the full path name. So don't expect finding ../src/routes/headers. You will find something like: /Users/david/git/...
My algorithm won't detect exported attributes like var Schema = require('mongoose').Schema. It is possible to make a function which is capable of this, but it will suffer many issues.
From within a module (doesn't work at the REPL) you can...
console.log( global.process.mainModule.filename );
And you'll get '/home/ubuntu/workspace/src/admin.js'

Load "Vanilla" Javascript Libraries into Node.js

There are some third party Javascript libraries that have some functionality I would like to use in a Node.js server. (Specifically I want to use a QuadTree javascript library that I found.) But these libraries are just straightforward .js files and not "Node.js libraries".
As such, these libraries don't follow the exports.var_name syntax that Node.js expects for its modules. As far as I understand that means when you do module = require('module_name'); or module = require('./path/to/file.js'); you'll end up with a module with no publicly accessible functions, etc.
My question then is "How do I load an arbitrary javascript file into Node.js such that I can utilize its functionality without having to rewrite it so that it does do exports?"
I'm very new to Node.js so please let me know if there is some glaring hole in my understanding of how it works.
EDIT: Researching into things more and I now see that the module loading pattern that Node.js uses is actually part of a recently developed standard for loading Javascript libraries called CommonJS. It says this right on the module doc page for Node.js, but I missed that until now.
It may end up being that the answer to my question is "wait until your library's authors get around to writing a CommonJS interface or do it your damn self."
Here's what I think is the 'rightest' answer for this situation.
Say you have a script file called quadtree.js.
You should build a custom node_module that has this sort of directory structure...
./node_modules/quadtree/quadtree-lib/
./node_modules/quadtree/quadtree-lib/quadtree.js
./node_modules/quadtree/quadtree-lib/README
./node_modules/quadtree/quadtree-lib/some-other-crap.js
./node_modules/quadtree/index.js
Everything in your ./node_modules/quadtree/quadtree-lib/ directory are files from your 3rd party library.
Then your ./node_modules/quadtree/index.js file will just load that library from the filesystem and do the work of exporting things properly.
var fs = require('fs');
// Read and eval library
filedata = fs.readFileSync('./node_modules/quadtree/quadtree-lib/quadtree.js','utf8');
eval(filedata);
/* The quadtree.js file defines a class 'QuadTree' which is all we want to export */
exports.QuadTree = QuadTree
Now you can use your quadtree module like any other node module...
var qt = require('quadtree');
qt.QuadTree();
I like this method because there's no need to go changing any of the source code of your 3rd party library--so it's easier to maintain. All you need to do on upgrade is look at their source code and ensure that you are still exporting the proper objects.
There is a much better method than using eval: the vm module.
For example, here is my execfile module, which evaluates the script at path in either context or the global context:
var vm = require("vm");
var fs = require("fs");
module.exports = function(path, context) {
context = context || {};
var data = fs.readFileSync(path);
vm.runInNewContext(data, context, path);
return context;
}
And it can be used like this:
> var execfile = require("execfile");
> // `someGlobal` will be a global variable while the script runs
> var context = execfile("example.js", { someGlobal: 42 });
> // And `getSomeGlobal` defined in the script is available on `context`:
> context.getSomeGlobal()
42
> context.someGlobal = 16
> context.getSomeGlobal()
16
Where example.js contains:
function getSomeGlobal() {
return someGlobal;
}
The big advantage of this method is that you've got complete control over the global variables in the executed script: you can pass in custom globals (via context), and all the globals created by the script will be added to context. Debugging is also easier because syntax errors and the like will be reported with the correct file name.
The simplest way is: eval(require('fs').readFileSync('./path/to/file.js', 'utf8'));
This works great for testing in the interactive shell.
AFAIK, that is indeed how modules must be loaded.
However, instead of tacking all exported functions onto the exports object, you can also tack them onto this (what would otherwise be the global object).
So, if you want to keep the other libraries compatible, you can do this:
this.quadTree = function () {
// the function's code
};
or, when the external library already has its own namespace, e.g. jQuery (not that you can use that in a server-side environment):
this.jQuery = jQuery;
In a non-Node environment, this would resolve to the global object, thus making it a global variable... which it already was. So it shouldn't break anything.
Edit:
James Herdman has a nice writeup about node.js for beginners, which also mentions this.
I'm not sure if I'll actually end up using this because it's a rather hacky solution, but one way around this is to build a little mini-module importer like this...
In the file ./node_modules/vanilla.js:
var fs = require('fs');
exports.require = function(path,names_to_export) {
filedata = fs.readFileSync(path,'utf8');
eval(filedata);
exported_obj = {};
for (i in names_to_export) {
to_eval = 'exported_obj[names_to_export[i]] = '
+ names_to_export[i] + ';'
eval(to_eval);
}
return exported_obj;
}
Then when you want to use your library's functionality you'll need to manually choose which names to export.
So for a library like the file ./lib/mylibrary.js...
function Foo() { //Do something... }
biz = "Blah blah";
var bar = {'baz':'filler'};
When you want to use its functionality in your Node.js code...
var vanilla = require('vanilla');
var mylibrary = vanilla.require('./lib/mylibrary.js',['biz','Foo'])
mylibrary.Foo // <-- this is Foo()
mylibrary.biz // <-- this is "Blah blah"
mylibrary.bar // <-- this is undefined (because we didn't export it)
Don't know how well this would all work in practice though.
I was able to make it work by updating their script, very easily, simply adding module.exports = where appropriate...
For example, I took their file and I copied to './libs/apprise.js'. Then where it starts with
function apprise(string, args, callback){
I assigned the function to module.exports = thus:
module.exports = function(string, args, callback){
Thus I'm able to import the library into my code like this:
window.apprise = require('./libs/apprise.js');
And I was good to go. YMMV, this was with webpack.
A simple include(filename) function with better error messaging (stack, filename etc.) for eval, in case of errors:
var fs = require('fs');
// circumvent nodejs/v8 "bug":
// https://github.com/PythonJS/PythonJS/issues/111
// http://perfectionkills.com/global-eval-what-are-the-options/
// e.g. a "function test() {}" will be undefined, but "test = function() {}" will exist
var globalEval = (function() {
var isIndirectEvalGlobal = (function(original, Object) {
try {
// Does `Object` resolve to a local variable, or to a global, built-in `Object`,
// reference to which we passed as a first argument?
return (1, eval)('Object') === original;
} catch (err) {
// if indirect eval errors out (as allowed per ES3), then just bail out with `false`
return false;
}
})(Object, 123);
if (isIndirectEvalGlobal) {
// if indirect eval executes code globally, use it
return function(expression) {
return (1, eval)(expression);
};
} else if (typeof window.execScript !== 'undefined') {
// if `window.execScript exists`, use it
return function(expression) {
return window.execScript(expression);
};
}
// otherwise, globalEval is `undefined` since nothing is returned
})();
function include(filename) {
file_contents = fs.readFileSync(filename, "utf8");
try {
//console.log(file_contents);
globalEval(file_contents);
} catch (e) {
e.fileName = filename;
keys = ["columnNumber", "fileName", "lineNumber", "message", "name", "stack"]
for (key in keys) {
k = keys[key];
console.log(k, " = ", e[k])
}
fo = e;
//throw new Error("include failed");
}
}
But it even gets dirtier with nodejs: you need to specify this:
export NODE_MODULE_CONTEXTS=1
nodejs tmp.js
Otherwise you cannot use global variables in files included with include(...).

Categories