import from ES6 module to legacy js code - javascript

I use babel.js and have a new module foo in my code
foo.js:
export function foo(number) {
return number + 42;
}
And bunch of big old files where everything is global. And I need to call a foo function from that legacy code.
bar.js:
...
var result = foo(0);
...
But I can't just import foo cause then my bar.js will be a module and unavailable from other old code. Is there a way to import module
and retain my bar.js global?

I had a somewhat similar problem recently. I ended up polluting window object with everything I need in legacy code.
I created separate register.js module for this purpose and included it to my webpack build:
import ClassA from './ClassA'
import ClassB from './ClassB'
import * as utils from './utils'
Object.assign(window, utils)
Object.assign(window, {ClassA, ClassB})

Related

Define global variable before importing ES6 module

I have recently switched from CommonJS to ES6 modules in my NodeJS project. One of the challenge I'm facing is to define a global variable before I import one of my module. I used to do that with CommonJS in my main file:
const path = require('path');
global.appRoot = path.resolve(__dirname);
const myObj = require('./my-object-file');
where my my-object-file uses global.appRoot.
With ES6, I have tried the following:
import path from 'path';
global.appRoot = path.resolve(path.resolve());
import myObj from './my-object-file';
with my-object-file.js being:
export default {
root: global.appRoot
}
But I get undefined for global.appRoot in my-object-file.js.
What is going on here?
Are import modules called before anything in my code?
How can I solve this (knowing that I absolutely want to be able to define the path as a global variable accessible in my modules)?
Are import modules called before anything in my code?
Yes, all module imports are resolved before any code in the importing module runs.
However, imported modules are also executed in order, so if you do
import './setupGlobals';
import myObj from './my-object-file';
then the setupGlobals module code is executed before the my-object-file one. So it will work when do
// setupGlobals.mjs
import path from 'path';
global.appRoot = path.resolve(path.resolve());
I absolutely want to be able to define the path as a global variable accessible in my modules
No, you really don't want to do that. Instead of a global variable that might be created anywhere, explicitly declare your dependency!
If you have a separate module to define your globals anyways, just make that export those variables instead of putting the values on the global object:
// globals.mjs
import path from 'path';
const appRoot = path.resolve(path.resolve());
export { appRoot as default }
Then you can declaratively use this global constant in any of your modules:
// my-object-file.js:
import appRoot from './globals';
export default {
root: appRoot
}
I like Bergi's answer with:
import appRoot from './globals';
Then, any file that wants to can get access to appRoot and you preserve modularity.
But, in addition to that approach, my suggestion in comments was that rather than setting a global before you import, you export a function from your module and call that function, passing it the desired path. This is a general purpose way of passing initialization parameters to a module from the parent module without using globals.
And, I suggest you use the import.meta.url work-around for creating the equivalent of __dirname as outline here. What you were doing with path.resolve() is just getting you the current working directory, which is not necessarily __dirname as it depends upon how this module was loaded for whether they are the same or not. Besides if you just wanted the equivalent of cwd, you could just use process.cwd() anyway in your child module. Here's the equivalent for __filename and __dirname in an ESM module.
// create __dirname and __filename equivalents in ESM module
import { fileURLToPath } from 'url';
import { dirname } from 'path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
Then, import the module initialization function (sometimes called a module constructor) and call the module constructor:
import myObjConstructor from './my-object-file';
const myObj = myObjConstructor(__dirname);
Then, inside of my-object-file, you export a function and when that function is called, you initialize your module using the passed in __dirname and return the myObj.
So, inside of my-object-file:
function init(__dirname) {
// do whatever you want for module initialization
// using __dirname
return yourObj;
}
export { init as default };

100% ESM module world - what it mean?

While reading about tree shaking in webpack documentation, I came across this sentence:
In a 100% ESM module world, identifying side effects is straightforward. However, we aren't there just yet.
What do they mean by "100% ESM module" and how it is different from the current import and export that we already use today?
reference: https://webpack.js.org/guides/tree-shaking/#mark-the-file-as-side-effect-free
The documentation you're reading is contrasting two types of scripts:
Scripts which expose everything they do through what they import and export
Scripts which do something in addition to importing and exporting (this could be seen as a "side effect")
Consider a big library, one that installs itself as a namespace on the global object to expose its functionality. Let's say that jQuery did this - that is, that it runs something like
const jQuery = (function() {
// lots and lots of code
})();
export default jQuery;
window.jQuery = jQuery;
This contains a side-effect: the line
window.jQuery = jQuery;
This means that other parts of your application could use window.jQuery without jQuery being specifically imported into that module. For example, you might have
// someModule.js
export const doStuff = () => {
window.jQuery('div').text('Hi!');
};
And this can work without the line
import jQuery from 'jQuery';
inside the module script, because jQuery is on the window. (For this to work, there needs to be at least one module somewhere that does import 'jQuery'; or something like that, so that jQuery's code that assigns itself to the window runs)
Because of the side-effect, Webpack will have a harder time with automatic tree-shaking - you'll have to explicitly note which modules depend on modules with side-effects.
In contrast, a module without dependency side-effects would be the someModule.js example above: all it does it export a function, without adding or changing functionality elsewhere.
So by "100% ESM module", Webpack is probably referring to scripts for which all modules' dependencies are explicit with import statements, instead of having to depend on side-effects (like a non-imported module assigning something to the window).
There are two popular module syntax nodejs use.
Commonjs: https://nodejs.org/dist/latest-v14.x/docs/api/modules.html
// exporting
module.exports.a = 1
// or, exports is an alias to module.exports, for all differences check out docs
exports.a = 1
// you can assign module.exports object as well, this sets what's exported
module.exports = {
b: 2
}
// a is not exported anymore
// importing default import, imports module.exports object
const a = require('./b')
// or via named import
const {c} = require('./b');
ES modules: https://nodejs.org/dist/latest-v14.x/docs/api/esm.html
// names export
export const a = 1;
// default export
export default const b = 2;
// importing via name
import {a} from './c'
// importing default export
import c from './b'
Commonjs and esm are still in use. So we are not in %100 esm world yet.

Meteor: importing whole module doesn't work

I am trying to make my Meteor code ES6 compatible. I had a file called /both/global.js containing functions and constants that I wanted to be accessible globally. For ES6 purposes, I moved it to /both/imports/global.js and prefaced all the functions and constants with export const.
I haven't changed the whole directory structure yet. The template javascript files are still in /client/controller/. I have added to them import statements for the exported functions and constants. When I write the import statements in the form:
import { fn1, fn2, ... } from '../../both/imports/global.js';
they work fine.
I would rather import all the functions and constants with a single:
import from '../../both/imports/global.js';
but that doesn't seem to do anything.
What am I doing wrong?
It's to do with the way imports and exports work.
What you need is
import * from '/both/imports/global.js'
Alternatively...
import something from '/both/imports/global.js'
Will look for the default export, and assign it to the variable called something.
import { fn1, fn2, ... } ...
Will import the named variables into your namespace
Another way is to do this:
import globalFunctions from '/both/imports/global.js'
Then your functions are callable like this:
globalFunctions.fn1()
globalFunctions.fn2()

Can Typescript import CommonJS Modules?

I had this file:
//foo.js
var foo = function () {
return "foo";
};
module.exports = foo;
So, I wanted to import it to my Typescript file. I tried this
//typescript.ts
import * as foo from ("./foo");
Didn't work. I read about this 'ambient' modules, so I added this
//typescript.ts
/// <reference path="./foo.d.ts" />
import * as foo from ("./foo");
And I added a "foo.d.ts" file in the same folder, which had the purpose of letting typescript know about the types of my imported function:
declare module "foo"
{
function foo(): string
export = foo;
}
No luck.
I thought that the problem was with the import syntax (you cannot have the same syntax for es6 and commonjs modules, right?). So I did this.
import foo = require("./foo");
As you might guess, that didn't work either.
I have been able to import d3 an use it successfully when I installed it as a node module with npm install d3 and referenced its d.ts file. I did it with this code:
import * as d3 from "d3";
I haven't been able to do the same with any other module (jquery, box2d, box2d-commonjs, among others), nor with my own libraries as demonstrated above. I am new to Typescript, so probably I'm missing something very obvious, but I haven't been able to figure it out by myself.
Turns out it was something really obvious: you had to use the --allowJs option. This worked for me:
tsc --moduleResolution "node" --module "commonjs" --allowJs main.ts
Though, I still can't figure out why d3 worked while the others libraries didn't.

ES6 import "module" don't give acces to function of "module"

I am trying to use ES6 imorts with babel and Node.js
import "./utils.js";
log("something"); //throw an error : log is not defined
My utils.js look like this :
function log(... params){
console.log(... params);
}
log("module utils executed");
//Is executed and display "module utils executed" in the console.
I have also tryed to use export function log(... params) and export default log(... params) but it doesn't works.
So I don't understand how this is suppose to works...
EDIT:
I know that an other way to import is to do import utils from "./utils.js"
But it's not what I want. I want to be able to use log() without prefixing it with the module variable name. Like in this blog post
there Different ES6 import and Node.js require Question Describe The Difference
In case you will use Node.js require:
your utils.js File will be
function log(params) {
console.log(params);
}
module.exports = {
log
};
The other File will import your Utils Module will be
var utils = require('./utils');
utils.log("test");
In case you will use ES6 Modules:
your utils.js File will be
var log = function (params) {
console.log(params);
}
var utils = {
log: log
}
export default utils;
The other File will import your Utils Module will be
import utils from 'utils';
utils.log("test");
UPDATE
According to your Comment, Yes you can do this But using ES6 Module
your utils.js File will be
function log(params) {
console.log(params);
}
function anotherLog(params) {
console.log(params);
}
export { log, anotherLog }
The other File will import your Utils Module will be
import { log } from 'utils';
log("test");
No, there is no way to import all exported members of a module into the current namespace. Importing a module for side effects (i.e. import 'utils') does nothing with the members of utils.
The closest you can get is something like this:
utils.js
export function log(...params) { ... }
export function foo(a) { ... }
main.js
import * as u from './utils';
u.log(1, 2, 3);
u.foo(4);
or
import { log, foo } from './utils';
log(1, 2, 3);
foo(4);
One of the design goals of the ES6 module spec is a static module structure, which allows resolution of imports at compile time (before executing anything). Allowing blanket imports would make static analysis more difficult.
EDIT (do not do this!)
As #Bergi pointed out in the comments, you can add functions to the global namespace as follows:
utils.js
function log(...params) { ... }
global.log = log;
main.js
import './utils'; // import for side effects, add properties to the global object
log(1, 2, 3); // the global object is in the scope chain, so this is resolved
However this is a bad idea. Global variables are slow to resolve and in general break the modularity you try to achieve by using modules in the first place.

Categories

Resources