Loading dependencies with or without react - javascript

I have an old IIFE that is injected into legacy pages via <script src.
However, I want to use all these old libraries in a react app. I just need to use the global function exposed.
I figure loading dependencies that will work both via script or via react's import or nodejs require
Here is an example of an example IIFE
example.js :
var $ = $;
var geocomplete = $.fn.geocomplete;
var OtherExternalLib = OtherExternalLib;
var Example = (function() {
return {
init: function () {
// stuff
}
}
)();
Where the legacy code is calling Example.init(), and likewise the react code will call the same function.
Where $ (jQuery), $.fn.geocomplete, and OtherExternalLib are all dependencies that must be loaded, either they should be loaded on-demand or just throw a big loud error message.
I suspect if the solution loads dynamically example.js would look something like
var $ = load("\libs\jquery.js");
var geocomplete = load("\libs\$.fn.geocomplete.js");
var OtherExternalLib = load("\libs\OtherExternalLib.js");
var Example = (function() {
return {
init: function () {
// stuff
}
}
)();
And the legacy application can still use <script src=example.js and React can use
import {Example} from example
Understandably this is somewhat a round-about way to of using legacy code in new applications, so I am open to other ideas on how best to expose an IIFE (with or without dependencies) and using it in React

I am using react+typescript in my project with some limitations which is why I had to dynamically import my package (my project runs in a shell project with AMD module, not having my own startup, and change the way project files get bundled).
Since I could only use the dependent modules on the fly during the run time, I had to assume them were valid while building and bundling . Most of them were IIFE.
So I used the lazy dynamic import .
something like this
import("somePolyfill");
This would be translated by TSC
new Promise(function (resolve_3, reject_3) { require(["arrayPolyfill"], resolve_3, reject_3); });
This would call the IIFE and execute the polyfills or initializing any window or global variable, so the rest of the code is aware of that.
If it returns a module or throughs error can be handled like normal promise then and catch.
So I created a wrapper
export class DepWrap {
public static Module: any = {};
public constructor() {
this.getPI();
this.getSomeModule();
}
public async getPI() {
DepWrap.Module["PI"] = 3.14;
}
public async getSomeModule() {
await import('somepath/somemodule').then(($package) => {
DepWrap.Module["somemodule"] = $package;
}).catch(() => {
window.console.log("Some Module error");
});
}
}
this got compiled to
define(["require", "exports", "tslib"], function (require, exports, tslib_1) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var DepWrap = /** #class */ (function () {
function DepWrap() {
this.getPI();
this.getSomeModule();
}
DepWrap.prototype.getPI = function () {
return tslib_1.__awaiter(this, void 0, void 0, function () {
return tslib_1.__generator(this, function (_a) {
DepWrap.Module["PI"] = 3.14;
return [2 /*return*/];
});
});
};
DepWrap.prototype.getSomeModule = function () {
return tslib_1.__awaiter(this, void 0, void 0, function () {
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, new Promise(function (resolve_1, reject_1) { require(['somepath/somemodule'], resolve_1, reject_1); }).then(function ($package) {
DepWrap.Module["somemodule"] = $package;
}).catch(function () {
window.console.log("Some Module error");
})];
case 1:
_a.sent();
return [2 /*return*/];
}
});
});
};
DepWrap.Module = {};
return DepWrap;
}());
exports.DepWrap = DepWrap;
});
with this I could use all the dependency modules from my wrapper and every time i need to import a new one I would create another function to add that to my wrap module.
import { DepWrap } from "wrapper/path";
const obj = new DepWrap(); // initialize once in the beginning of project so it would import all the dependencies one by one .
Afterwards in all file, I can import my module from the wrapper
import { DepWrap } from "wrapper/path";
const { PI, somemodule} = DepWrap.Module;
I am not sure if the code will work for your scenario as well, but I guess tweaking it a bit might come in handy for your useCase .
Plus : if you are writing unit test case it will help jest to just ignore the modules and can create a mock for this so that you can test your actual code .

Related

How to import a function to test?

I am learning how to test Javascript code in Vanilla Javascript so I am following a tutorial. My project structure looks like this:
Modules/File.js,File2.js (each file contains a function I want to test)
app.js (a file for testing which works together with test.js file)
index.html
index.js (currently I keep my main code there)
test.html (I use it to run test, according to the tutorial)
test.js (this is where I describe tests)
I want to be able to import functions from File.js, and File2.js in app.js, so I can run tests. How do I achieve that?
P.S. I am using es6 modules import/export. But I would like to see how can I run imported functions for test. Maybe using $myapp global variable for it since thats how its done in the tutorial.
test.js:
(function () {
'use strict';
/**
* test function
* #param {string} desc
* #param {function} fn
*/
function it(desc, fn) {
try {
fn();
console.log('\x1b[32m%s\x1b[0m', '\u2714 ' + desc);
} catch (error) {
console.log('\n');
console.log('\x1b[31m%s\x1b[0m', '\u2718 ' + desc);
console.error(error);
}
}
function assert(isTrue) {
if (!isTrue) {
throw new Error()
}
}
it('test if its a string', function () {
assert($myapp.isValidString(2))
})
})();
app.js file:
(function () {
'use strict';
// Create a global variable and expose it to the world
var $myapp = {};
self.$myapp = $myapp;
$myapp.isValidString = function (value) {
return typeof value === 'string' || value instanceof String;
}
})();
From what I understand, you want to import JS functions from one file into another file.
ES6 introduced the module's functionality which allows the import/export of Javascript functions between different files.
Below is a simple example:
// helloworld.js
export function helloWorld() {
return 'Hello World!';
}
// main.js
import helloWorld from './helloworld.js';
console.log(helloWorld());

How to correctly export a javascript module in Webpack project

I am currently experiencing issues exporting a module in webpack. I have been able to export simple modules which contain functions like the following:
let getEle = function(item) {
return document.getElementById(item);
};
module.exports = {
getEle: getEle
};
And in my main.js I will import it like so:
import { getEle } from './helper.js';
This works without any issues. However, I was trying to export a custom datePicker that I found (namely FooPicker: https://github.com/yogasaikrishna/foopicker):
var FooPicker = (function () {
// code
function FooPicker() {
// code
}
return FooPicker;
})();
// my attempt at exporting the entire FooPicker module
module.exports = {
FooPicker: FooPicker
}
And I try to import it like so in my main.js:
import FooPicker from './fooPicker.js'
My attempt at using the module (this works as expected if I simply call the function in a demo HTML file):
let foopicker2 = new FooPicker({
id: 'datepicker2'
});
However this does not work and I seeing the following error:
Uncaught TypeError: FooPicker is not a constructor
I have limited experience working with Webpack and I have done a fair bit of searching but I am still not able to find something relevant to my issue. What am I doing incorrectly here and what can I do to correct it?
Your case is with export
var FooPicker = (function () {
// code
function FooPicker() {
// code
}
return FooPicker;
})();
var fooPicker = new FooPicker()
console.log(fooPicker)
Try:
module.exports = FooPicker
const FooPicker = require('./fooPicker.js')
var fooPicker = new FooPicker()
This will work

mocha test (and babel) using global variables

I am writing library using es6, transpiling it with babel via webpack and npm.
The problem is, that my lib is dependent on some code, that I can not change but need to use. I don't know how to load var stuff (from the following example) in my tests so that it is visible for the module.
See the example:
external-stuff.js - this one can not be changed and is loaded before my lib is loaded on prod env.
var stuff = {
get some() { return "some"; }
get stuff() { return "stuff"; }
}
some-module.js - this is one of the modules in the library
export class foo {
static get whatever() { return stuff.some; }
static get whichever() { return stuff.stuff; }
}
test
import {foo} from "../src/foo.js";
let assert = require('assert');
describe('foo', function() {
describe('#whatever()', function() {
it("should do some", function () {
assert.equals(foo.whatever(), "some");
});
});
});
If I run this I get "ReferenceError: stuff is not defined"
I already tried to define "stuff" in before() hook, but no success.
In the end I found a solution that's 'good enough'. I am not sure it would be sufficient for some advanced library though.
I have created file called globals.js
var g = typeof(window) === 'undefined' ? global : window;
// Dependencies - add as many global stuff as needed
g.stuff= typeof(stuff) === 'undefined' ? {} : stuff;
And I import this 'es6module' at the beginning of test
import * as globals from "../lib/global/globals"
import {foo} from "../src/foo.js";
And then I use node-import npm module with which I load the global to the tests in beforeEach hook.
beforeEach(function () {
global.stuff = imports.module("lib/global/stuff.js").stuff;
});
This is perfect for unit testing because I can also mock stuff. And its even more awesome because this way I have a place where I 'define' global dependencies. It would be nice to improve on it and make it per es6modul dependencies... and build on it something fancy... ya know.. dependency injection.
complete test
require('node-import'); // +
import * as globals from "../lib/global/globals"; // +
import {foo} from "../src/foo.js";
let assert = require('assert');
describe('foo', function() {
beforeEach(function () { // +
global.stuff = imports.module("lib/global/stuff.js").stuff; // +
}); // +
describe('#whatever()', function() {
it("should do some", function () {
assert.equals(foo.whatever(), "some");
});
});
});

How to get js function into webWorker via importScripts

I have a worker.js file:
self.importScripts('/static/utils/utils.js')
onmessage = (e) => {
let a = e.data[0]
let b = e.data[1]
let c = func1(a,b)
postMessage(c)
}
The utils.js file looks something like this:
module.exports = {
func1: function(a,b){
return a+b
}
I keep getting error:
Uncaught ReferenceError: module is not defined
at utils.js:1
Obviously require, and import and any other server side imports aren't working but I'm not sure why it's having a problem with my importScripts - https://developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope/importScripts
The correct solution is to pack your worker with webpack. If you don't want to do that, read below.
I usually write myself a polyfill for node require:
// This will not work in normal UI thread
// None of this should make it into production
function require(moduleName) {
self.module = { exports: null };
// Nasty sttuff right here, probably should throw error instead
if (moduleName == "fs")
return null;
// This part is especially unprofessional
if (!moduleName.endsWith(".js"))
moduleName += ".js";
importScripts(moduleName);
return self.module.exports;
}
This makes use of the fact that importScripts is synchronous. Note that this will still cause errors if you try to load native node modules (eg. fs) or if other module properties are used.
Try this utils.js:
(function () {
self.func1 = function (a, b) {
return a + b
}
}());
Try to do this:
//inside worker.js
self.addEventListener("message",(event)=>{
importScripts("module.js")
utils.print1()
utils.print2()
})
//inside module.js
//setting a variable in global scope, allows worker.js to use it.
var utils = {
print1(){
console.log("This is a content from a module.")
},
print2(){
console.log("This is a another content from a module.")
}
}

How can I call a function that's created inside a chain of other functions?

I have created the following:
module Admin.Grid {
export function addGridControls() {
$('#createLink')
.click(function () {
var $link = $(this);
$link.prop('disabled', true);
adminDialog($link);
return false;
});
}
}
This is converted to:
var Admin;
(function (Admin) {
(function (Grid) {
function addGridControls() {
$('#createLink').click(function () {
var $link = $(this);
$link.prop('disabled', true);
adminDialog($link);
return false;
});
Previously when it was not inside a module I called the function like this:
$(document).ready(function () {
"use strict";
addGridControls()
});
Now it's inside of a module what's the best way for me to call this
function so it gets executed every time the document is ready?
One way of doing this is, is to add the function to some object.
var Admin = {};
(function (Admin) {
(function (Grid) {
Admin.addGridControls = function () {
....
And call it like
$(document).ready(function () {
"use strict";
Admin.addGridControls()
});
As #Mike Lin has commented, you need to import the module.
Working in TypeScript (and assuming AMD module format, with your module in another file), you can do it like this:
import g = module('path-to-admin-grid-module');
$(document).ready(() => {
"use strict";
g.Admin.Grid.addGridControls();
});
Otherwise, if you're just using internal modules within the same file, it's as simple as:
$(document).ready(() => {
"use strict";
Admin.Grid.addGridControls();
});
The latter case is nicely previewed in the Walkthrough: Modules example here: http://www.typescriptlang.org/Playground/
There's a pretty good example of the former here: TypeScript compile AMD modules with required defines and AMD is covered in more detail here: http://requirejs.org/docs/whyamd.html

Categories

Resources