I'm working on a Manifest v3 browser extension where I need to identify the browser in which the extension is currently running from the backgroundScript. Since ManifestV3 extension uses a service worker, it doesn't have DOM or window. So I'm not able to use window.navigator.userAgent.
I found a related question which talks about how to gets window height and width information, but I couldn't find any other information to fetch the userAgent of the browser.
Is this possible?
Neutral globals
Things like navigator aren't specific to visual representation of a window.
Just omit window. and read it directly:
navigator
navigator.userAgent
atob
fetch
Window-specific globals
Things specific to user interaction or visual/aural representation like DOM or AudioContext, or those that may show a prompt for user permissions.
Not available in a worker.
Aliases for window
Use them instead of window for code clarity or if a local variable is named just as a global property.
Built-in globalThis (Chrome/ium 71+, FF 65+) and self
These are worker-compatible aliases for the global scope. Note that a JS library you're loading may redefine them theoretically, but that'd be really weird and abnormal.
Self-made global
The most reliable method, but you'll have to add 'use strict' only inside an IIFE not globally.
This is already offered by bundlers like webpack.
Here's how you can replicate it yourself:
const global = (function(){
if (!this) throw "Don't add 'use strict' globally, use it inside IIFE/functions";
return this;
})();
Related
I have to deal with a node.js server project that uses global variables for common APIs. For instance in the entry point server.js there is a Firebase variable for the real-time database that is stored like this:
fireDB = admin.database();
I wasn't aware that this is possible and I would consider this a bad approach, but now I have to deal with it.
I'm not really interested to re-write any of the many calls to this variable in all those files, rather I would find a way to make fireDB show me suggestions only by changing this variable or installing an extension.
I tried to define it on top of the file as var fireDB, but then suggestions only work in the same file, not in others.
When I set a dot behind admin.database() the suggestions work, when I write fireDB. I get no suggestions, yet the call seems to be possible. Suggestions need to work in other files, too. How can I get this to work?
WARNING: MAKE SURE YOU UNDERSTAND THE PROBLEMS WITH GLOBALS BEFORE USING THEM IN A PROJECT
The above warning/disclaimer is mostly for anyone starting a new project that might happen across this answer.
With that out of the way, create a new .d.ts file and put it somewhere with a descriptive name. For example, globals.d.ts at the top level of the directory. Then just populate it with the following (I don't have any experience with firebase, so I had to make some assumptions about which module you're using, etc.):
globals.d.ts
import { database } from "firebase-admin";
declare global {
var fireDB: database.Database;
}
IntelliSense should then recognize fireDB as a global of the appropriate type in the rest of your JavaScript project.
Why does this work? IntelliSense uses TypeScript even if you're working with a JS project. Many popular JS packages includes a .d.ts file where typings are declared, which allows IntelliSense to suggest something useful when you type require('firebase-admin').database(), for example.
IntelliSense will also automatically create typings internally when you do something "obvious", e.g. with literals:
const MY_OBJ = { a: 1, b: "hello"};
MY_OBJ. // IntelliSense can already autocomplete properties "a" and "b" here
Global autocompletion isn't one of those "obvious" things, however, probably because of all the problems with global variables. I'd also guess it'd be difficult to efficiently know what order your files will run in (and hence when a global might be declared). Thus, you need to explicitly declare your global typings.
If you're interested in further augmenting the capabilities of IntelliSense within your JS project, you can also use comments to explicitly create typings:
/**
* #param {String[]} arrayOfStrings
*/
function asAnExample(arrayOfStrings) {
arrayOfStrings. // IntelliSense recognizes this as an array and will provide suggestions for it
}
See this TypeScript JSDoc reference for more on that.
I'm working with a Node project using VSCode. I'd like to be able to follow the tree upwards from functions/files at a lower level. That is, if I have an exported function doSomething in file dosomething.js, I'd like to see what code calls this function, and/or see what code requires this file.
// index.js
const { doSomething } = require('./tricky-stuff')
// tricky-stuff.js
const doSomething = function() {}
module.exports = {doSomething}
If I'm browsing tricky-stuff.js, is there a built-in feature to VSCode that allows me to see usages of either the function doSomething or where the file tricky-stuff.js is required/imported, so that it would show me the file index.js in this case (as well as perhaps other files where tricky-stuff is used)? Is there any extension? I seem to recall that WebStorm could do this, but I can't remember. (I know I could search for strings, but that seems inelegant).
I think it largely depends on how much VScode knows about your code, via the TypeScript language service or JSDoc comments etc...
Typically you can right click on a function and select the peek all references or find all references options:
In this case, it will show you all the places where that function is called or referenced.
Experimenting with a bootstrapped extension, I'm trying to understand the scopes and/or persistence of jsm modules by setting a property, called baseUri, on a module object from bootstrap.js and reading it again from javascript in my options.xul (which is opened from the Add-ons Manager).
My current understanding is that JavaScript Code Modules are persisted, once loaded. However, when I try to access baseUri from options.xul, its value is undefined.
install.rdf:
<!-- just the relevant XML (this works as expected, by the way): -->
<em:optionsURL>chrome://test/content/options.xul</em:optionsURL>
/modules/Test.jsm:
var EXPORTED_SYMBOLS = [ 'Test' ];
Test = {
baseUri: undefined
}
/bootstrap.js:
// this is done in global scope,
// not inside install() or startup() for instance, if that matters
let test = Components.utils.import( 'file:///absolute/path/to/Test.jsm', {} ).Test;
test.baseUri = someBaseUriIExtracted;
/chrome/content/options.js (included in /chrome/content/options.xul):
let test = Components.utils.import( 'file:///absolute/path/to/Test.jsm', {} ).Test;
console.log( test.baseUri ); // undefined
So, I guess what I'm failing to fully understand is what the exact scopes are from which I should be able to access object properties from exported jsm symbols and/or how and when exactly these objects are persisted.
Does my problem have anything to do with sand-boxing, perhaps? Does Firefox consider options.xul, when opened from the Add-ons Manager, to be a different security scope than bootstrap.js, perhaps?
Can you shed a thorough light on the actual scopes of jsm modules and when and where I should be able to access persisted properties on jsm modules?
The documentation is pretty straightforward about what and how is shared
Each scope that imports a module receives a by-value copy of the
exported symbols in that module. Changes to the symbol's value will
not propagate to other scopes (though an object's properties will be
manipulated by reference).
I think the accompanying examples are clear.
Maybe you should use getters/setters.
From what I know:
Other jsm modules
Browser window
Content window
bootstrap addon scope
I'm new to node js. I searched a lot on stack overflow on this question below, none what I need.
I have an app.js file which initiates node server and a router file. I want to be able to store a global value once and shared across other server side .js files which contains my functions. I also want this variable to be accessible in my .jade file. (I use express BTW)
Is there a way to accomplish this?
Thanks.
The Node.js documentation says under Module Caching
Caching Modules are cached after the first time they are loaded. This means (among other things) that every call to require('foo') will
get exactly the same object returned, if it would resolve to the same
file.
Multiple calls to require('foo') may not cause the module code to be
executed multiple times. This is an important feature. With it,
"partially done" objects can be returned, thus allowing transitive
dependencies to be loaded even when they would cause cycles.
If you want to have a module execute code multiple times, then export
a function, and call that function.
Which means you can easily expose a global object simply by putting it in its own module.
//config.js
var config = {
dbUrl: 'mogodb://localhost:2107/persons'
};
module.exports = config;
And then when you want to gain access to that object, you simply do:
var config = require('./config');
And that's done, you get access to the same instance everywhere.
You'll want to limit the usage of global vars in Node. This is because unlike any other server side language, Node is a persistent process that share all request. So you cannot setup user state globally as those will be shared across all user accessing your site.
In raw node, there's two global context:
global.foo = 'bar';
// and the process object
process.some_var = 1;
In Express, you can setup application wide vars using app.set
But, most of the time you'll want to share data by adding them to the request or the response objects. That is because those objects are "user" specifics, unlike the global namespace.
For the template, you'll always want to pass in the context:
app.render('email', Object.assign( aSharedObject, {
specific: 'values'
}));
i would use process.env or if you are using nconf put it into the app configuration as Jordan said, globals are BAD idea, also if you don't want to include nconf or any other conf module or use process.env then you can create a module and export a set of getters and setters to handle the value
Here's an example
$ cat main.js
App = {
version : 1.1
};
require('./mymod.js');
$ cat mymod.js
console.log(App.version);
$ node main.js
1.1
Note how I declared App in main.js without var. This allowed me to access App from mymod.js without having to call require. If I declare App with a var, this won't work.
I want to understand why this happens? Is it the intended behaviour for node.js or a bug? Is this behavior consistent with ECMAScript or CommonJS standards?
This trick gives a powerful mechanism to circumvent the require module system of node.js. In every file define your objects and add them to the top level App namespace. Your code in other files will be automatically have access to those objects. Did I miss something?
If you assign a variable without using var, it is automatically a global variable. That's just the way JavaScript works. If you put 'use strict'; (quotes required) at the top of your js file, this becomes an error instead.
All has to do with local scope vs global scope.
You can even do this (which is much neater):
app.js:
exports = {
version : 1.1
};
main.js:
var App = require('./app.js');
console.log(App.version);
Defining a variable without a preceding var will place it into the global namespace which is visible to all of your JavaScript code.
While this may seem a useful feature, it is generally considered bad practice to "pollute" the global namespace and can lead to subtle, hard-to-locate bugs when two non-related files both rely upon or define variables with the same name.
In nodeJS environment there is a global scope referenced by 'global' , just like the way we have 'window' in browser environments. In effect every javascript host enviroments always start with creating a global object.
When require('main.js') is executed, there is this following function that is created and executed against the global scope 'global'.
(function(exports,...){
//content of main.js
App = {
version : 1.1
};
})(module.exports,..);
When the above function is executed and since there is no var declaration for App , a property with name 'App' is created on global object and assigned the value.This behavior is according to ECMA spec.
That is how the App gets into global scope and is accessible across all modules.
require has been introduced to standardize development of modules that can be ported and used.