webpack execute module in global scope - javascript

Is there any way to have webpack execute module in the global scope? Specifically, my use case is the following library:
https://github.com/AzureAD/azure-activedirectory-library-for-js/blob/master/lib/adal.js
where there is the following code:
var AuthenticationContext;
if (typeof module !== 'undefined' && module.exports) {
module.exports.inject = function (conf) {
return new AuthenticationContext(conf);
};
}
As you can see the module is exporting an injection function (not sure why they don't just export the class). I am able to construct a new AuthenticationContext object successfully using the using the injection function. However, some of the functionality in this library relies on a global AuthenticationContext class and it errors out when window.AuthenticationContext === undefined. I would like to bundle this module with webpack but somehow, I need to ensure that AuthenticationContext will be available in the global scope. Is there any way to do that?
I have read about the ProvidePlugin but, as I understand the ProvidePlugin just takes an exported value and attaches it to the global scope. In this case I need to ensure that a non-exported value will be available in the global scope.
The most obvious solution is just to execute this module in the global scope. However, I would like this module to be part of the bundle. How can I accomplish this?
Thanks in advance.

I think I solved this with the following code:
import {inject} from 'adal-angular/lib/adal.js';
import config from './auth-config';
export default class Authenticator {
constructor() {
this.authContext = inject(config);
window.AuthenticationContext = this.authContext.constructor;
}
}
basically, I am exposing the constructor manually.

Related

Uncaught TypeError: Assignment to constant variable in javascript module

I have declared a variable in one module like this:
// first.js
var folder;
export {folder};
and I want to use it from a different module, like this:
// second.js
import { folder} from '../js/first.js';
folder = gui.addFolder( 'Avatar Measurements' );`
TypeError: Assignment to constant variable.
Imports are read-only live bindings to the original variable in the exporting module. The "read-only" part means you can't directly modify them. The "live" part means that you can see any modifications made to them by the exporting module.
If you have a module that needs to allow other modules to modify the values of its exports (which is/should be rare), it needs to export a function that does that. For instance:
a.js:
export let folder;
export function setFolder(f) {
folder = f;
}
b.js:
import { folder, setFolder } from "./a.js";
console.log(folder); // This will be `undefined` unless another module has already modified it
setFolder(gui.addFolder("Avatar Measurements"));
console.log(folder); // This will be whatever `gui.addFolder` returns
Again, though, it is/should be very rare for a module to allow other modules to modify its exports that way.
In the comments you asked me for an example of exporting an object for this instead:
a.js:
export let shapes = {};
b.js:
import { shapes } from "./a.js";
shape.folder = gui.addFolder("Avatar Measurements");
But it's not clear to me why that object needs to live in a rather than just being local in b.
That happens because you are creating a folder constant when importing folder from ../js/first.js. You cannot reassign a value to any constant, including folder. Don't use == or ===, as those are comparasion operators and don't change the value of folder.
If you want to pass information from second.js to first.js, consider exporting a function from second.js. If you don't, then use another name for a variable, like folder_ (don't forget to declare it first: var folder_;).

Import UMD Javascript Modules into Browser

Hi I am doing some research on RxJS. I am able to use the library simply by referencing it in my browser as such:
<script src="https://unpkg.com/#reactivex/rxjs#5.5.6/dist/global/Rx.js"></script>
It imports with the global object namespace variable of 'Rx'. I can make observables and do all the fun stuff.
Where everything breaks down is when I change the src to point to the latest UMD file like this <script src="https://unpkg.com/rxjs/bundles/rxjs.umd.js"></script>
The import seems to not be working as exported object functions don't seem to exist?
There is a specific function I am trying to use called 'fromEvent' that allows an observable to be created from any DOM event.
I am getting an error when using the latest RxJS 6.2.2 UMD file.
Why is this? If you look inside the js file at the bottom you can see the export of the function and at the top of the file you see the global namespace called 'rxjs'.
I am not using any loaders like requirejs nor do I have any experimental browser features enabled. I am not using any 'import' statements.
I am simply trying to reference the global namespace of the script object. The syntax for the module definition is identical except for Rx vs rxjs.
To replicate the error, simply create an Observable.fromEvent(.... and watch the error console.
Thanks!
Here's a oneliner to import UMD modules using browser modules and dynamic imports in 2020.
export default async (url, module = {exports:{}}) =>
(Function('module', 'exports', await (await fetch(url)).text()).call(module, module, module.exports), module).exports
Usage example:
const ednToJS = await importUMD(`https://unpkg.com/edn-to-js#0.1.2/dist/main.js`)
const rxjs = await importUMD('https://unpkg.com/rxjs#6.6.3/bundles/rxjs.umd.js')
tada
Recently the UMD bundle was renamed to just rxjs, see https://github.com/ReactiveX/rxjs/commit/556c904ea61a8424e5d24f170b20eadbc05d01f0#diff-6d2911fe563068b8126098588db98a84
If you want to use RxJS 6 you need to switch to "pipable" operators (and creation functions), see https://github.com/ReactiveX/rxjs/blob/master/docs_app/content/guide/v6/migration.md#operator-pipe-syntax
So for example this works:
<script src="https://unpkg.com/rxjs/bundles/rxjs.umd.js"></script>
<script>
rxjs.fromEvent(document, 'click').subscribe(console.log);
</script>
Demo: https://stackblitz.com/edit/rxjs6-demo-r2rtbz?file=index.html
export default async function (url, module = {exports:{}})
{
const response = await fetch(url);
const script = await response.text();
const func = Function("module", "exports", script)
func.call(module, module, module.exports);
return module.exports;
};
Same code reformatted
Here is an example after doing the proper import , note the pipe.
submission = rxjs.fromEvent($('#mybutton'), 'click')
.pipe(rxjs.operators.map((event) => {
return "something"
}));
I may rename the globals to 'r' and 'ro' to avoid the new verbosity.
Also bonus points if someone can point to piped error handling in 6.0!
It imports with the global object namespace variable of 'Rx'.
Maybe version 5.5.6 does but the latest version which you're trying to use, 6.2.2, does not. The object it exports to the global namespace is called rxjs. If you load https://unpkg.com/rxjs/bundles/rxjs.umd.js in a browser you'll see this in the source in the UMD module definition:
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory):
(factory((global.rxjs = global.rxjs || {})));
}(this, (function (exports) { 'use strict'; // etc
If you want to use fromEvent you can do so via rxjs.fromEvent.
To import any umd module from unpkg or jsdeliver in the browser with JavaScript, what I find mostly useful was using d3#require. Since actually importing the umd modules give me a bare "Module" object.
In your case it would work like this, notice I dont actually use any HTML, as a bonus this would also work for deno
import("https://raw.githack.com/d3/d3-require/main/src/index.mjs")
.then(
_=>_.require("https://unpkg.com/#reactivex/rxjs#5.5.6/dist/global/Rx.js")
)
.then(Object.keys)
.then(console.log)
Now, here I show an use case of require, where I require 2 modules, one is the Rx library, and the other is just an inspector library that shows off elements as if it were in the console
import("https://raw.githack.com/d3/d3-require/main/src/index.mjs").then(async _=>{
var inspector=await _.require("#observablehq/inspector");
var Rx=_.require("https://unpkg.com/#reactivex/rxjs#5.5.6/dist/global/Rx.js");
var x=new inspector.Inspector(window.inspect);
x.fulfilled(await Rx)
return x;
})
#import url("https://raw.githack.com/observablehq/inspector/main/src/style.css")
<div id="inspect" />

Referencing global libraries from an ember component

I am using ember-remarkable because I want to add both markdown parsing and syntax highlighting to my project. The only thing it's missing is line numbers, which I want to add by using this library: highlightjs-line-numbers.js
I override ember-remarkable's md-text component with the following:
import MDTextComponent from 'ember-remarkable/components/md-text';
export default MDTextComponent.extend({
tagName: null,
didRender() {
this._super(...arguments);
this.$("code[class*='language-']").each((i, block) => {
this.$(block).addClass('hljs');
hljs.lineNumbersBlock(block);
});
}
});
Everything works! However, my editor complains that hljs is not defined, even though it is available on the page.
How do I avoid this error?
If it is ES6 modules, just import it. Otherwise you can use it as window.hljs.
When you run your js code in browser, window is the root of the global variables. So you can access it as window.hljs.
If you run your code in Fastboot, this should be a better way to access global objects: (window || Fastboot || global).hljs.
On the other hand, instead of using the global object container, you can just prevent linter to give this error. For eslint, see this configuration: Specifying Globals.
You should explicitly import your dependencies, if for no other reason than to communicate to future readers. import hljs from 'highlightjs-line-numbers'

Duplicate identifier error with d.ts file and nodejs require with same name

Now I am sure the issue is because there is a d.ts file included which contains a module called "Shared", and a require statement which includes a variable of the same name if it is being used in a NodeJS environment.
// shared.d.ts
declare module Shared { ... }
// other_module.ts
/// <reference path="shared.d.ts"/>
if(require) { var Shared = require("shared"); }
export class Something {
public someVar = new Shared.SomethingElse("blah");
}
So when I compile other_module.ts (which is actually a lot of separate files), it tells me Shared is a duplicate identifier, which I can understand as TS thinks Shared is a module, but then is being told it is the return of require.
The problem here is that the output of modules need to be compatible with nodeJS's require system, so in this case when other_module is required it will be in its own scope and will not know about Shared.SomethingElse so the require is needed so the internal modules in other_module will be able to access the Shared library, but in the browser environment it would get Shared.SomethingElse via the global scope.
If I remove the reference then the file wont compile as it doesn't know about Shared, if I remove the require when the module is loaded into nodejs (var otherModule = require("other_module")) it will complain that it doesn't know about Shared. So is there a way to solve this?
First the error
Duplicate identifier because you have Shared in shared.d.ts + in other_module.ts.
FIX A, be all external
If you want to use amd / commonjs ie. external modules, you need to use import/require (not var/require like you are doing). Using an import creates a new variable declaration space and therefore you are no longer polluting the global namespace Shared from other_module.ts. In short :
// shared.d.ts
declare module Shared {
export function SomethingElse(arg:string):any;
}
declare module 'shared'{
export = Shared;
}
And a typesafe import:
// other_module.ts
/// <reference path="shared.d.ts"/>
import Shared = require("shared");
export class Something {
public someVar = new Shared.SomethingElse("blah");
}
FIX B, as you were, but you need to use a different name then
Inside other_module don't use the name Shared locally if local scope is global scope. I recommend you just use external everywhere and compile for node with commonjs and browser with amd as shown in fix A, but if you must here is a compile fixed other_module.ts.
// other_module.ts
/// <reference path="shared.d.ts"/>
var fooShared: typeof Shared;
if(require) { fooShared = require("shared"); }
else { fooShared = Shared; }
export class Something {
public someVar = new fooShared.SomethingElse("blah");
}

What does it mean to "export to the browser"

The backbone.js annotated source describes the following piece of code
var Backbone;
if (typeof exports !== 'undefined') {
Backbone = exports;
} else {
Backbone = root.Backbone = {};
}
as "The top-level namespace. All public Backbone classes and modules will be attached to this. Exported for both CommonJS and the browser."
What does "exported for the browser" mean in this context?
In CommonJS, your modules are sequestered and anything you want to share with the thing that requires you is shared through the "exports" variable. Node.js, for instance, uses this.
On the other hand, if you are just in the browser, then you don't use the exports variable and you add a new variable in root which ultimately points to the window global var.
In other words, if we are using something that supports CommonJS, export Backbone. If not, put it in the root context instead.

Categories