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.
Related
This is just something I thought today and I didn't see a lot of information so I'm going to share this weird cases and how I personally solved them (if there's a better way please comment, but meanwhile this might help others ^^)
In a regular module, you would do something like this to export your function/library/object/data:
// regular NodeJS way:
module.exports = data;
// ES6 way
// (will get transpiled to the regular way using the module variable by webpack)
export data;
default export data;
When compiling the library usually babel or tsc are used, but if for any reason you want not only to compile (transpile) your library but also pack it using webpack, you will encounter this case.
As you know, in a webpack bundle the module variable is local to the bundle (every module/file gets wrapped with a function where module is a parameter = local variable), so nothing really gets exported outside the bundle, is just nicely managed by webpack.
That means that you can't also access the contents using the regular require/import methods.
In some case you might find necessary to export outside webpack. (i.e. you are trying to build a library using webpack and you want it to be accessible by other people). This basically means you need to access the original module variable, but webpack doesn't expose it like it happened with __non_webpack_require__.
See also: Importing runtime modules from outside webpack bundle
The solution is to create our own __non_webpack_module__ (as webpack does with __non_webpack_require__.
How I did it is using webpack.BannerPlugin to inject some code outside the bundle. This code is prepended to the build after the minification is done, so it's preserved safely.
In your webpack.config.js:
plugins: [
new BannerPlugin({
raw: true,
banner: `const __non_webpack_module__ = module;`,
}),
]
And again, if you are using TypeScript, in global.d.ts:
declare const __non_webpack_module__: NodeModule;
And now, you can do something like this in your code:
__non_webpack_module__.exports = /* your class/function/data/whatever */
This will allow to import it as usual from other files
Tip: You might want to look at BannerPlugin to check other options, like include or exclude so this variable is only generated on the desired files, etc.
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.
Inside a jest test, I need to import a javascript module that declare some global functions. This javascript module is autogenerated from django (ex jsi18n) and it's an auto invoking function
(function(globals) {
var django = globals.django || (globals.django = {});
...
}(this));
This is helpful for use the translation inside the react component. For example including a translation string in our .jsx file using a global defined function gettext()
<p>{ gettext('got it') }</p>
I've tried to import the module using the standard form
import './djangojs';
but jest report the error
TypeError: Cannot read property 'django' of undefined
because this it's undefined (strict mode). So I've tried to manual edit the module and adding at the end }(this || window)); and works correctly.
But the module is autogenerated every time. So how I can bind this to window for using the global object without manually editing the file?
Solution:
I have imported the django javascript modules directly using the setupFiles as #dfsq told
"setupFiles": [
"<rootDir>/path/to/jsi18n/djangojs.js",
"<rootDir>/path/to/js/reverse.js"
],
The problem is specific to Babel. Babel is commonly used in Jest testing rig, and it is likely used here.
Babel transform-es2015-modules-commonjs enables strict mode for both ES module imports and require, so this is undefined in imported module.
In order to make a piece of code executed in loose mode while its context is in strict mode, it should be evaluated in global context:
const fs = require('fs');
const script = fs.readFileSync(require.resolve('./djangojs')).toString();
new Function(script)(); // or (0, eval)(script);
instead of import './djangojs' or require('./djangojs').
It's expected that script doesn't have to be transpiled (it will be evaluated as is) and doesn't rely on the context (doesn't use require, because it's not available in global context). I.e. eval will work for regular script like ./djangojs but not for CommonJS module.
This may be useful if a module shouldn't be available globally or may vary between tests.
If it should be available for all tests then setupFiles should be used, as another answer suggests.
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).
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.