Access javascript objects from dev console after bundling - javascript

I'm working on a React app and learning the ropes. I have a SPA where my dev environment is all running in Docker, and is using Gulp + Watchify, Browserify, and Babelify to bundle up all my JS(X) code and get it to the browser as a "bundle.js".
Everything works well, except I cannot access objects like React and ReactDOM from my Chrome developer tools. I'm sure this is because they are being "hidden" due to how they are being bundled up, but I'm not sure how to fix that or gain access to them. I can always "window.ReactDOM = ReactDOM" in my code, but that seems like the dumb way to go, and I foresee wanting to play with more code in the console.
I noted that I can add "debugger" to my code, and these objects are available when the code is paused, but once it runs I can't access the objects.
How are these objects being hidden away, and is there a sane way to get access to them besides breakpoints?

How they are hidden:
It is often times useful to not leak variables to the window/global scope. This is important because different files and different libraries could have the same variable names and conflict with each other in. A very simple trick usually used is doing something like:
// Runs an inline anonymous function
(function(){
/// Make your variables and run your code here ...
var a = 1; // Only accessible in this function
})();
console.log(a) // Undefined
^ The above is a common 'wrapper' to see around a regular JS file.
If you examine the output of webpack or another bundler, they take this a step further and make a closure for each file. So the bundle will look something like:
(function(){
var files = [
function(module, require){ var ReactDOM = require('react-dom')... /* file one code */}
function(module, require){ ... module.exports = localReactDomVar ... }
]
// Webpack magic runs each file and recursively gets dependencies on calls to require...
})();
TLDR: Each module is intentionally hidden and is anonymous anyway until the point in your code at which you do var React = require('react'). If you need access to React and ReactDOM in your console, there is no better way than window.React = React etc.

Related

Instantiate an object defined in module A and use its methods in module B javascript

I'm working in a web development project.
Right now i'm using a 3rd party library to instantiate an object of that library in a file called, let's say, fileA.js
so i do:
import libraryExport from "./librarymain.js"
var object = libraryExport( ... );
export default object;
Now, in fileB.js i want to use the methods that the instantiated object has, for example:
import object from "fileA.js"
object.methodOfTheLibrary();
However, when im running this in my browser console i always get "methodOfTheLibrary is not a function", which means, from my point of view, that the library is not being imported properly in fileB.js
Note: I'm using webpack to bundle all of my files and everything was compiling and bundling just fine until i came with issues. I usually know my way around C++ in an advanced way but for JS i just still don't fully understand how to solve these kind of import issues.
Thank you for any help
It's generally* recommended that you avoid using an imported module directly in the body of another module. (One of your own modules, that is... as you'll see in a moment, third-party modules are generally fine to use.)
The big issue is load order. If your fileA also imports some other module, which imports another module which then imports fileB, then fileB will attempt to run and, since fileA is still trying to load dependencies, object will not actually have been instantiated yet.
You'll either need to carefully review your dependencies to look for just such a loop and then eliminate it or, if possible, restructure fileA to wrap your code in a function that can be called once the entry point has finished loading (which will guarantee that all other modules have been resolved):
// fileB.js
import object from "fileA.js"
export function init() {
object.methodOfTheLibrary();
}
// main.js
import init from "fileB.js";
init();
* "generally" meaning that there are plenty of perfectly acceptable situations where it's fine. You just have to mindful of the pitfalls and mitigate against those situations.

Node vs React: functions in generic javascript modules

I have a Node server with a utils file that looks like this:
update = () => {
//
};
module.exports = { update };
This works, but when I use the same file in a React app, I get the error
"'update' is not defined no-undef"
To fix this I must add const keyword. This makes no sense to me. Why is declaring the function needed only in React?
This is because the React you're using has come bundled with a linter which warns you of potential code quality problems. create-react-app is one of the React bundles which comes integrated with ESLint.
If you don't want to see the warning and don't want to use ESLint, you can build your project manually from scratch, rather than using a boilerplate setup like create-react-app.
If you installed ESLint for your Node project, you'd see the no-undef warning for Node code as well.
Note that what you're seeing is a linter warning, not a runtime error. If you ignore the warning and bundle and serve the project anyway, it will be still be runnable if it doesn't run in strict mode. (In strict mode, the use of an undefined variable without declaring it with const / let / var first will throw an error)
The linting rule being violated in the code is described at length here:
https://eslint.org/docs/rules/no-undef
For further reference on what's going on when you don't declare a variable (and why it probably isn't a good idea), see
What is the purpose of the var keyword and when should I use it (or omit it)?
If you're using modules, you should pretty much always be using explicit imports and exports only - don't implicitly create global variables (like you're currently doing with update = () => {), since that defeats the point of using modules.

How can I use functions and objects without require them in NodeJS

I have been studying NodeJS for about a year and today I found something strange for me. We all know that in order to use a module (function, object or variable) in another module we must export and import it except for the native modules like String, Number, Promise, etc. I installed an external package for unit testing called Jest.
The strange thing here is that I have created two test modules called: logic.js and logic.test.js, in none I have imported the Jest module, however I can access all its methods. Let's show some code:
logic.js
module.exports.add = function(a, b){
return a + b;
}
logic.test.js
const lib = require('../logic')
test('Add - Should return the sum of two numbers', () => {
const result = lib.add(1,3);
expect(result).toBe(4);
});
As you can see in logic.test.js I have access to expect and test methods and i have not impoted nothing about Jest.
The questions here are:
How is this posible?
Its a good practice to do this with my modules
As Jonas W stated in the comments, they make use of the global variable that is common to all your application.
The use of the global variable is very simple
test.js
global.myObject = 'MyMan'
app.js
require('./test')
console.log(myObject)
Loading app.js will render MyMan
You might say that I actually import the test module and that Jest does not.
The thing is that you execute your node application using node yourFile.js but you instanciate your jests tests with the jest command line.
It's the jest command line that handles the binding between its framework (the expect and test methods.) and your script.
Is it a good practice?
I would say no. Except if you plan to make a library like Jest that have its own command line launcher and that you want to give tools like that to the users of your library.
The power of Node lives into the module organization, don't be afraid to use them.

Global variable not working in NodeJS

I am trying to get global variables working in node.js, but it seems like I don't really understand the concept, even though my understanding matches the documentation.
Minimal example
My main.js file, which is compiled using rollup is:
global.a = 1;
require('./core/test.js');
My core/test.js file is simply:
console.log(a);
This causes the error:
Uncaught ReferenceError: a is not defined
The fully compiled output is:
(function (exports) {
'use strict';
var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
console.log(a);
commonjsGlobal.a = 1;
}((this.LaravelElixirBundle = this.LaravelElixirBundle || {})));
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjpudWxsLCJzb3VyY2VzIjpbIkM6L3dhbXAvd3d3L3V1YmMvcmVzb3VyY2VzL2Fzc2V0cy9qcy9jb3JlL3Rlc3QuanMiLCJDOi93YW1wL3d3dy91dWJjL3Jlc291cmNlcy9hc3NldHMvanMvYXBwLmpzIl0sInNvdXJjZXNDb250ZW50IjpbImNvbnNvbGUubG9nKGEpO1xyXG4iLCJnbG9iYWwuYSA9IDE7XHJcbnJlcXVpcmUoJy4vY29yZS90ZXN0LmpzJyk7XHJcbiJdLCJuYW1lcyI6WyJnbG9iYWwiXSwibWFwcGluZ3MiOiI7Ozs7O0FBQUEsT0FBTyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQzs7QUNBZkEsY0FBTSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUM7OyJ9
It seems that a gets defined after console.log(a);. I'm not 100% sure if this matters in JS but it could be causing the error.
So my question is: what causes this, and what am I doing wrong here?
Possibly relevant information: I'm using Laravel Elixir to compile everything.
Note: Please don't post discussion on whether or not to use global variables in node.js or in general. I know it's usually a bad idea, but I have a very good reason to do it in this case.
Edit: Additional context
What I'm really trying to do is getting Zurb Foundation to work with Laravel Elixir though node.js. I installed foundation-sites through NPM. foundation-sites relies on jquery, which it pulls in itself. However, Foundation doesn't seem to follow the usual conventions of just using something like var jquery = require('jquery'); in its own js file. It actually relies upon a global jQuery variable being available. Therefore I have to ensure that somehow.
My actual file looks like this:
global.jQuery = global.$ = require('jquery');
require('foundation-sites');
So if there are any Foundation/Laravel-specific anwers I would be very happy to hear them as well. Is there something I'm not getting, or did Foundation just not create their package the "right" way?
It looks to me like an issue with your bundler not respecting the order you're doing those two lines in. Your source is clearly:
global.a = 1;
require('./core/test.js');
...but the bundled result is clearly the other way around:
console.log(a);
commonjsGlobal.a = 1;
It seems like it's hoisting the require calls. In general, having require calls that need to be done at a particular point in the execution control flow is not best practice (in fact, when defining modules for ES2015, import is explicitly defined as not being executed in the module's step-by-step control flow).
So if you need to do this, you'll probably want to look at another bundler. But check that it supports order other than dependency resolution order.
You are right that it should work. Here's a working example:
// file1.js
global.a = 'hello';
require('./file2');
// file2.js
console.log(a);
Run node file1.js, and you'll see 'hello' printed to the console.
It looks like you've got some other tool being used (Laravel / Elixir). Use node directly and see what happens.
Alright I solved this in a pretty simple way, by using browserify instead of rollup. Thanks for the other answers pointing me in the right direction. It's not a perfect solution, and I still don't fully understand what the differences are between the two, but it'll do for this project.
The relevant part of my gulpfile:
mix.browserify('app.js','public/js/app.js');
To use browserify with Laravel Elixir, I used
npm install laravel-elixir-browserify-official --save-dev
I solved this for rollup by providing an output.intro that sets up the required variables. In your case this would be:
// rollup.config.js
export default {
// ...
output: {
// ...
intro: `let a = 1;`
}
};
Admittedly this qualifies as a hack, but solves the bundling problem in my case (where switching away from rollup is not an option).

How to use js modules from non-module files

I'm a beginner at using js modules.
I'm working on a fairly simple web application. It uses typescript and angular 2, which heavily relies on modules.
Most of my app ts files 'import' one or many js modules (usually mostly angular 2 modules).
As I understand, because my app ts files have a top level 'import', they are automatically considered a js module by typescript.
However, I want any of my app ts files to be accessible by any other of my app ts files, without having to 'import' each other. But because they are now modules themselves, ts requires me to do that...
Is it possible?
It seems crazy to me that for each of my app ts file, I should have to declare every other of my app ts files that are used in there (I like to have tiny files with a single class/interface). In addition, this relies on relative paths which breaks as soon as I restructure my folder structure.
Am I thinking about this the wrong way?
You must have a js file which is an entry point to your application right?.. So in that file just import all the modules which you want to access without importing and attach them to the window object. Since the window object is available globally, you can access your module from anywhere without importing the corresponding module. For example,
Consider this scenario:
You have a module in a file called module1.ts
The entry point of your application is a file called index.ts
And you have a module2 where you require something from module1
// module1.ts
function add(first: number, second: number): number {
return first + second
}
export {add}
in your index.ts
// index.ts
import {add} from '<path to module1>/module1';
window.add = add
Now in your module2
// module2.ts
window.add(1, 2)
Since the window object is available globally you can attach as many properties to it as you like.
As far as the type resolution is concerned you can declare a window module with the add function you require in a .d.ts file as follows:
declare module window {
add: (first: number, second: number) => number
}
Declaring dependencies (e.g modules) for each file is a double-edged sword.
The advantage is that there is no 'magic' - you know exactly where each function, variable, class etc. is coming from. This makes it much easier to know what libraries / frameworks are being used and where to look to troubleshoot issues. Compare it to opposite approach that Ruby on Rails uses with Ruby Gems, where nothing is declared and everything is auto-loaded. From personal experience I know it becomes an absolute pain to try to workout where some_random_method is coming from and also what methods / classes I have access to.
You're right that the disadvantage is that it can become quite verbose with multiple imports and moving relative files. Modern editors and IDEs like WebStorm and Visual Studio Code have tools to automatically update the relative paths when you move a file and also automatically add the imports when you reference code in another module.
One practical solution for multiple imports is to make your own 'group' import file. Say you have a whole bunch of utility functions that you use in all your files - you can import them all into a single file and then just reference that file everywhere else:
//File: helpers/string-helpers.ts
import {toUppercase} from "./uppercase-helper";
import {truncate} from "./truncate-helper";
export const toUppercase = toUppercase;
export const truncate = truncate;
Then in any other file:
import * as StringHelpers from "../path-to/helpers/string-helpers";
...
let shoutingMessage = StringHelpers.toUppercase(message);
The disadvantage of this is that it may break tree shaking, where tools such as webpack remove unused code.
Is it possible
Not in any easy way. The ts file is a module and uses e.g. module.exports (if commonjs) that will need to be shimmed out. And that is just the runtime story. The TypeScript story will be harder and one way would be to make a .d.ts file for the module stating the contents as global.
Like I said. Not worth doing. Modules are the way forward instead of making something hacky.
It's not crazy at all. You are definitively thinking in the wrong way.
Actually what you don't like it's a common feature in all modern programming languages and it makes the code and structure of the app a lot clearer and simple to understand.
Without imports and going to old school way looks very crazy to me :)
You can have only chaos with so many global variables.

Categories