Snippet
// Require Underscore, if we're on the server, and it's not already present.
var _ = root._;
if (!_ && (typeof require !== 'undefined')) _ = require('underscore');
This is directly from the development code. However I don't see how it can determine this by checking for underscore.js as this library runs on the both the client and the server.
(typeof require !== 'undefined') <-- key part of the code.
If you are not on the server (or you do not have require.js), this piece of code will not run, since the require variable will be undefined.
Related
I am trying to write a package for server and client use with as little modification as needed.
Some libs are part of Node but not included in a browser, others are available in a browser but not in Node.
Using https://github.com/lmaccherone/node-localstorage for example, I want to require the library when on Node, but in a browser, localStorage is already available.
I would like to check whether localStorage is available with a declare statement found in https://stackoverflow.com/a/65755447/2875404 so it looks like that:
declare var localStorage: any | undefined;
if (typeof localStorage === "undefined" || localStorage === null) {
console.log("need to use Node localStorage")
var LocalStorage = require('node-localstorage').LocalStorage;
var localStorage = new LocalStorage('./localStorage');
} else {
console.log("using Browser localStorage")
}
After wrapping up the package in webpack and, for a test, running it in a Chrome snippet, the "need to use Node localStorage" message pops up in console. If I manually edit the webpack'd code to console.log(localStorage) it actually does print the localStorage (so it must be there) - additionally, when I remove the whole if (typeof... block, the code accessing localStorage seems to run just fine.
What exactly do I need to do to make this "hybrid decision" function work? Can this have to do with webpack somehow putting things into a "deeper" scope that doesn't have access to window variables such as localStorage? But why is it possible to print and interact with the class then? I'm confused.
As I said in the comment, you could use the global window to check if you're in browser context:
declare var localStorage: any | undefined;
if (typeof window === 'undefined') {
console.log("need to use Node localStorage")
var LocalStorage = require('node-localstorage').LocalStorage;
var localStorage = new LocalStorage('./localStorage');
} else {
console.log("using Browser localStorage")
}
I have a webapp which will run on a website and as a standalone Electron instance (.exe for Windows).
I'd like to tell via JavaScript if the webapp is running inside ElectronJS or not, in order to display some extra features for the online version. Is there any way to detect the Electron framework instance? I'd like to avoid writing two slightly different versions of the webapp.
Just use this code (got it from is-electron "library")
function isElectron() {
// Renderer process
if (typeof window !== 'undefined' && typeof window.process === 'object' && window.process.type === 'renderer') {
return true;
}
// Main process
if (typeof process !== 'undefined' && typeof process.versions === 'object' && !!process.versions.electron) {
return true;
}
// Detect the user agent when the `nodeIntegration` option is set to true
if (typeof navigator === 'object' && typeof navigator.userAgent === 'string' && navigator.userAgent.indexOf('Electron') >= 0) {
return true;
}
return false;
}
Based on electron issue:
For main scripts, they're running as a Node process, so use process.versions.hasOwnProperty('electron') or equivalent
For renderer scripts, they're running in the browser, so use /electron/i.test(navigator.userAgent) or equivalent
Electron exposes full access to Node.js both in the main and the renderer process.
Source: Electron Application Architecture
This means that in your renderer thread (i.e. the user-facing part of your application) you can access NodeJS native modules such as fs.
I would recommend adopting a similar approach to browsers: avoid user-agent-based conditionals and prefer a is-this-feature-available? model if you can:
// renderer.js
try {
const fs = require('fs');
// test something (quick) that only the `fs` module can do
} catch (e) {
// probably not an electron app
}
However please do read https://www.electronjs.org/docs/tutorial/security first!
Further thoughts
Presumably you must somehow derive two artefacts from one single codebase: a web app and a desktop app.
You could perhaps consider injecting/exposing an environment variable during the build process assuming you can run two different build configurations.
you can use for simplicity a library named is-electron
I'm currently getting more familiar with Node.js and I'm building out a restful API. I have a config file where I store things like the environment name (staging v prod), the port number to run my server on and a hashing secret (using sha256 to hash a user pwd).
Everything is defined except for the hashing secret (it's the last key:value entry in my object). Logging out other values works just fine - trying to log out the hashing secret just says undefined. I've been going crazy for the last 2 hours here.
config.js
var environments = {}
environments.staging = {
'httpPort': 3000,
'httpsPort': 3001,
'envName': 'staging',
'hashingSecret': 'thisIsASecret'
};
environments.production = {
'httpPort': 5000,
'httpsPort': 5001,
'envName': 'production',
'hashingSecret': 'thisIsAlsoASecret'
};
var passedEnvironment = typeof(process.env.NODE_ENV) === 'string' ? process.env.NODE_ENV.toLowerCase() : ''
// check that passed in NODE_ENV value exists in our config container object
var configEnvironment = environments.hasOwnProperty(passedEnvironment) ? environments[passedEnvironment] : environments.staging
module.exports = configEnvironment
and then my file where I have helper functions (such as a function to hash a user pwd) looks like this:
// Dependencies
var crypto = require('crypto')
var config = require('./config')
// Container for our helper functions
var helpers = {}
// Define helper functions
helpers.hash = function(str) {
// validate our string input
if (typeof(str) == 'string' && str.length > 0) {
var hash = crypto.createHmac('sha256', config.hashingSecret).update(str).digest('hex')
return hash
} else {
return false
}
}
I'm constantly seeing
TypeError: Key must be a buffer
referring to the config.hashingSecret OR, if I try to log out the hashing secret BEFORE using it to create a hash, I get 'undefined'.
I'm going absolutely crazy - particularly because all of the other object properties ARE defined. To me that means that the module is exporting correctly and the object is initialized.
Using typeof tells me that config is an object - not sure where to go from here.
UPDATE -
I figured out the solution to my issue - I was requiring an old (cached) version of my config module. I had changed the project directory structure and neglected to update all of my require statements - I was making changes to the config file in the new location but the project was continuing to use the cached file from the old location. As a result, any changes made to the file weren't being served to the project (even though they'd appear in code as I typed them out in VSCode).
I am working on a JavaScript library for JSON/XML processing. My library works in browser as well as Node.js (with xmldom and xmlhttprequest modules).
One of the users recently asked for RequireJS support. I have taken a look at the RequireJS/AMD thing and think it is a good approach so I'd like to provide this.
However I'd like to retain the portability: my library must work in browsers (with and without RequireJS) as well as Node.js. And in the browser environment I don't depend on xmldom or xmlhttprequest since these things are provided by the browser itself.
My question is: how can I implement my library so that it works in browsers as well as in Node.js with an without RequireJS?
A bit of historyand my current solution
I initially wrote my library for browsers. So it just created a global-scope object and put everything inside it:
var Jsonix = { ... };
Later on users asked for Node.js support. So I added:
if(typeof require === 'function'){
module.exports.Jsonix = Jsonix;
}
I also had to import few modules mentioned above. I did it conditionally, depending on whether the require function is available or not:
if (typeof require === 'function')
{
var XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest;
return new XMLHttpRequest();
}
Now there's this story with RequireJS. If RequireJS is present then the require function is present as well. But module loading works differently, I have to use the define function etc. I also can't just require things since require has an async API in RequireJS. Moreover, if my library is loaded via RequireJS, it seems to process the source code and detects require('something') even if I do it conditionally like
if (typeof require === 'function' && typeof require.specified !== 'function) ...
RequireJS still detects require('xmlhttprequest') an tries to load the corresponding JS file.
Currently I'm coming to the following solution.
// Module factory function, AMD style
var _jsonix = function(_jsonix_xmldom, _jsonix_xmlhttprequest, _jsonix_fs)
{
// Complete Jsonix script is included below
var Jsonix = { ... };
// Complete Jsonix script is included above
return { Jsonix: Jsonix };
};
// If require function exists ...
if (typeof require === 'function') {
// ... but define function does not exists, assume we're in the Node.js environment
// In this case, load the define function via amdefine
if (typeof define !== 'function') {
var define = require('amdefine')(module);
define(["xmldom", "xmlhttprequest", "fs"], _jsonix);
}
else {
// Otherwise assume we're in the RequireJS environment
define([], _jsonix);
}
}
// Since require function does not exists,
// assume we're neither in Node.js nor in RequireJS environment
// This is probably a browser environment
else
{
// Call the module factory directly
var Jsonix = _jsonix();
}
And this is how I check for dependencies now:
if (typeof _jsonix_xmlhttprequest !== 'undefined')
{
var XMLHttpRequest = _jsonix_xmlhttprequest.XMLHttpRequest;
return new XMLHttpRequest();
}
If I have require but not define then I assume this is a Node.js environment. I use amdefine to define the module and pass the required dependencies.
If I have require and define thet I assume this is a RequireJS environment, so I just use the define function. Currently I also assume this is a browser environment so dependencies like xmldom and xmlhttprequest are not available and don't require them. (This is probably nor correct.)
If I don't have the require function then I assume this is a browser environment without RequireJS/AMD support so I invoke the module factory _jsonix directly and export the result as a global object.
So, this is my approach so far. Seems a little bit awkward to me, and as a newbie to RequireJS/AMD I'm seeking advise. Is it the right approach? Are there better ways to address the problem? I'd be grateful for your help.
Take a look at how underscore.js handles it.
// Export the Underscore object for **Node.js**, with
// backwards-compatibility for the old `require()` API. If we're in
// the browser, add `_` as a global object.
if (typeof exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) {
exports = module.exports = _;
}
exports._ = _;
} else {
root._ = _;
}
...
// AMD registration happens at the end for compatibility with AMD loaders
// that may not enforce next-turn semantics on modules. Even though general
// practice for AMD registration is to be anonymous, underscore registers
// as a named module because, like jQuery, it is a base library that is
// popular enough to be bundled in a third party lib, but not be part of
// an AMD load request. Those cases could generate an error when an
// anonymous define() is called outside of a loader request.
if (typeof define === 'function' && define.amd) {
define('underscore', [], function() {
return _;
});
}
This is what I ended up with:
// If the require function exists ...
if (typeof require === 'function') {
// ... but the define function does not exists
if (typeof define !== 'function') {
// Assume we're in the Node.js environment
// In this case, load the define function via amdefine
var define = require('amdefine')(module);
// Use xmldom and xmlhttprequests as dependencies
define(["xmldom", "xmlhttprequest", "fs"], _jsonix_factory);
}
else {
// Otherwise assume we're in the browser/RequireJS environment
// Load the module without xmldom and xmlhttprequests dependencies
define([], _jsonix_factory);
}
}
// If the require function does not exists, we're not in Node.js and therefore in browser environment
else
{
// Just call the factory and set Jsonix as global.
var Jsonix = _jsonix_factory().Jsonix;
}
Here is a template I'm currently using, it's both AMD and node compatible though not directly loadable stand-alone in the browser...
The main advantage to this approach is that the domain-specific code does not need to care about what imported it, for the general case.
/**********************************************************************
*
*
*
**********************************************************************/
((typeof define)[0]=='u'?function(f){module.exports=f(require)}:define)(
function(require){ var module={} // makes module AMD/node compatible...
/*********************************************************************/
/*********************************************************************/
/**********************************************************************
* vim:set ts=4 sw=4 : */ return module })
I'm working on a project that uses Node.js. I'm familiar with JavaScript, but not great. As part of that, I've run into a challenge that I'm not sure how to overcome.
I need to share some code on the server (Node.js) and my client-side (browser) app. I want to be able to access this code by typing the following:
myCompany.myProject.myFunction(someValue);
or just
myProject.myFunction(someValue);
In an attempt to do this, I have the following:
'use strict';
var myCompany = myCompany || {};
var myProject = myCompany.myProject || {};
myProject.myFunction= function(someValue) {
console.log(someValue);
};
Inside of myFunction, I want to one thing if I'm running on the server (Node.js) and something different if I'm running in the browser. However, I'm not sure how to do that. I reviewed this post and this SO question, yet I still don't understand it.
Thank you for your help!
You need something like this:
function someFunctionName() {
// Common functional
if (typeof module !== 'undefined' && module.exports) {
// Do something only in Node.JS
} else {
// Do something else in the browser
}
// Common functional
}