Global variables are undefined for imported Javascript functions - javascript

Trying to figure out if there is a problem due to the import/export method, or if my architecture just bad....
Previously, I had multiple files of javascript. Just functions, no classes. In one "center/main" JS file, there are global variables. These variables are accessed and used/updated by functions in that same file, as well as other files. Each JS file had to have its own tag within the index.html
The move was then to switch to webpack as a module builder which would remove the need for all those script tags. Instead I just have to import/export the functions.
The problem is that now after using that method, the global variables are undefined to the imported functions Below is the setup dumbed down, but I don't see why it would be a problem. Maybe I'm missing something.
main JS file
import * as SettingsFile from './settings';
var myVariableUsed;
$(document).ready(function() {
myVariableUsed = "test";
SettingsFile.startSettings();
});
secondary JS file (settings.js)
export function startSettings(json) {
console.log(myVariableUsed);
}

Hy, i think you can understant what is happening with this article:
https://medium.com/webpack/brief-introduction-to-scope-hoisting-in-webpack-8435084c171f
To be short, webpack creates a new scope for required files, because of 'use strict' declaration on generated code output.
To pass parĂ¢meters to required modules you need to do do something like this:
// somefile
require("lib.js")(param1, param2)
// lib.js
module.exports = function(param1, param2) { }

Related

Referencing vanilla js file in TypeScript getting file is not a module

I'm trying to import a file into TypeScript that's basically just a js file that you'd put into a tag. I've tried a few different things.
// global.d.ts
declare module 'myfile.js'
Inside of the react file:
// component.tsx
import { foo } from '../lib/myFile.js' // This is saying it is not a module
Inside of the js file, it looks like this a few times so not sure how I need to reference the file:
(function( something ) {
something.Foo = function (){}
}(window.something = window.something || {}));
Any thoughts on how I could use this file? Do I need to go through and declare typings for everything in it?
EDIT: I've added allowJS to my tsconfig but it still doesn't work.
You can only import what is exported from the file.
If your file contains only immediately invoked functions, or top level code, you only need to import the file itself like this:
import '../lib/myFile.js'
This is a little weird, however. I would suggest wrapping everything with a function and exporting then importing that function instead.

How to package object prototypes in separate file

I have created a number of String.prototype functions which for maintainability I'd like to have in its own file. That is, I'd like to include the file in a javascript project and thus have all the String functions defined.
I could create a module that exports each function, but then I'd have to assign each function as its own String prototype, yes? Something like
var myStringFunctions = require("myStringFunctions");
String.prototype.func1 = myStringFunctions.func1;
Is there a way to include such a file so that the prototypes are defined as part of the inclusion?
Try it, you will see your code and using require("./myStringFunctions"); works just fine.
./myStringFunctions.js
String.prototype.func1 = function() {
return this.toUpperCase(this);
};
./index.js
require("./myStringFunctions");
console.log("foo".func1()); // FOO
If your JS is going to run in the browser, you can use JS modules with the import and export syntax if you use a module bundling build tool like Webpack: https://webpack.js.org/ .
If your JS is running in a Node.js environment, modules are supported: https://www.w3schools.com/nodejs/nodejs_modules.asp

Webpack function import not accessible by JS on page

First time learning webpack with Laravel 5.5.
I have a helpers.js library which I include into my app.js bunlde like this:
import * as $mh from './helpers.js';
An example function in helpers.js is:
export function valRemoveHighlight(element)
{
$(element).closest('.form-group, .input-group, .has-feedback').removeClass('has-error').addClass('has-success');
}
I can then access this function inside the bundle like this:
$mh.valRemoveHighlight(element);
But I have JS script at the bottom of my page which cannot access the function in the bundle. I have tried different scopes with no success:
window.$mh.valRemoveHighlight(element);
$mh.valRemoveHighlight(element);
valRemoveHighlight(element);
How do I make these exported / imported helper functions accessible in the global scope, so any inline / page script can access them?
Ideally kept within the helpers scope $mh. but accessible from the page / script like:
$mh.valRemoveHighlight(element);
Thanks!
$mh is only available inside of app.js, so you will have to expose that function yourself inside your app.js by doing : window.$mh = $mh

How should I define a global TypeScript variable in a definition file so that it can be imported?

I have an external JS library with a global parameter:
function Thing() { ... }
...
var thing = new Thing();
There is a TypeScript definition file, so in thing.d.ts:
declare var thing: ThingStatic;
export default thing;
export interface ThingStatic {
functionOnThing(): ThingFoo;
}
export interface ThingFoo {
... and so on
Then I import this into my own TS files with:
import thing from 'thing';
import {ThingFoo} from 'thing';
...
const x:ThingFoo = thing.functionOnThing();
The problem is that transpiles to:
const thing_1 = require("thing");
...
thing_1.default.functionOnThing();
Which throws an error. I've asked about that in another question, and the suggestion is to use:
import * as thing from 'thing';
That doesn't fix it - it gives me thing.default in TS but then that's undefined once transpiled to JS.
I think there's something wrong with thing.d.ts - there must be a way to define a typed global parameter that can be imported.
How should I write thing.d.ts so that it represents the JS correctly and doesn't transpile to include default or other properties not actually present?
If the only way to use that library is by accessing its globals (as opposed to importing it as node module or amd or umd module), then the easiest way to go is have a declaration file without any exports at top level. Just declaring a variable is enough. To use it, you have to include that declaration file when compiling your typescript code, either by adding it to files or include in tsconfig.json, or directly on command line. You also have to include the library with a <script> tag at runtime.
Example: thing.d.ts
declare var thing: ThingStatic;
declare interface ThingStatic {
functionOnThing(): ThingFoo;
}
declare interface ThingFoo {
}
test-thing.ts
const x:ThingFoo = thing.functionOnThing();
can be compiled together
./node_modules/.bin/tsc test-thing.ts thing.d.ts
the result in test-thing.js:
var x = thing.functionOnThing();
See also this question about ambient declarations.
Note: there are module loaders out there that allow using global libraries as if they were modules, so it's possible to use import statement instead of <script> tag, but how to configure these module loaders to do that is another, more complicated question.

Why can't I see a variable defined in b.js within a.js, despite including the former?

This is b.js
var something = "hooorraaaaay!!!";
And this is a.js
require( './b.js' );
console.log(something);
Why isn't something being recognised within a.js. I understand that it has been declared with var, but my understanding is that any variable declared outside a function should act global. So why isn't this working?
Node modules are each in their own scope. By default, nothing in module a is visible to module b. Doing a require() on a module is NOT like including the source. It loads the file runs the code and then the only things that are available to the outside world are:
Any functions or properties that are explicitly exported from the module by assigning to module.exports.
Any functions or properties that are explicitly assigned to the global object in node.
So, if you want something in b.js to be exposed to the outside world, you do like this:
// b.js
module.exports.callMe = function() {
console.log("I'm in module b");
}
And, then in a.js, you can do this:
// a.js
var b = require('./b.js');
b.callMe();
This is how the module system works in node.js. It is very different than just including a <script> tag in a browser web page. You can read more about the module system in these references:
Understanding module.exports and exports in Node.js
What is the purpose of Node.js module.exports and how do you use it?
Node.js Handbook - How require() Actually Works
Internally, node.js loads a module's code and then inserts it into a wrapper function. So, each top level variable in a node module is actually only a local variable in that module function. This is why nothing is globally declared or shared by default. It is done this way on purpose so that each module comes with it's own private area for storing it's state and will not, by default, interfere with the variables of any other module.
You then explicitly export only the property/function interfaces that you want to make public and even then, they are still not exported as public symbols so again they cannot conflict with anything else.
So, the b.js code above actually gets transformed into this when node.js runs it:
(function (exports, module, require, __filename, __dirname){
module.exports.callMe = function() {
console.log("I'm in module b");
}
})(...);
The (...) contains actual variables that are passed to the module.
Let me assume you are using node.js judging from the require function.
node.js wraps each file in its own scope. this has nothing to do with the use of var keyword. every file in node.js is called a module.
Now let's say you want to include a module, here comes require, you have used it right.
But since your module doesn't export anything, it's useless when it's included in some other module.
So at the end of your b.js file, add the following line :
module.exports.something = something;
Now we can finally use our exported variable :
var b = require('./b.js');
console.log('Something is : ' + b.something);

Categories