How to export objects from within ES6 modules - javascript

I am exporting a object as below:
//apiEndpoints.ts
import {CONSTANTS} from './constants';
let remote = CONSTANTS.API_ENDPOINT;
export const ENDPOINT = {
signup: remote + 'auth/signup',
checkusername: remote + 'auth/checkUsername/${username}'
};
Upon importing this module, ENDPOINT is not imported.
see attached error

You are using a transpiler. In the actual output code, there is no variable called ENDPOINT because the transpiler has renamed it. You are getting an error when you type that in the debugger console because it doesn't exist with that name. You can see in the scope inspector in the sidebar, the name you'd actually have to type is
apiEndpoints_1.ENDPOINT
where the name of the object is based off of the filename of the module being imported, and the property is the exported key. The renaming is done in this case to make sure that the code behaves properly to take ES6 live import bindings into account.

This is an Ionic v2 project. Ionic v2 uses the typescript transpiler.
The majority of the features that the transpiler works on are ECMAScript 2015 related.
The error I am referring to is about the module implementation in ECMAScript 2015. To learn more see
http://exploringjs.com/es6/ch_modules.html
Hence, in an ideal world, I should be able to export constants, variables, functions and classes. However for some reason when I attempt to export variables and objects .. it fails.
It does not fail on classes.
#loganfsmyth is right. It is importing the module, but renaming it and hence rendering it unusable.
I had to rethink the implementation and am using a different approach for now until I figure out a solution to this issue.
Thanks all who tried.

Related

Best way to evaluate javascript source code module with node.js

With node, I transpile a typescript tsx file int javascript. Then I need to call a function that is described in that javascript code. ES6 modules are used.
I use
writeFileSync('./temp/page1.js', js) // js is the module source code
import page from '../temp/page1.js'
page is here the default export which is a function that I want to call.
That works fine but I wonder if there is a better way to call the function inside the module source code? A way that avoids saving the source code into a temporary file.
eval would not be a security concern, but I think it would not give me the default export, modules are not considered in this old function.
Maybe something node related?
I found https://www.npmjs.com/package/node-eval which would avoid the file saving, although it still requires a filename, which causes some brittleness in my code.
node:module
node:vm
are maybe promising, looking at https://github.com/node-eval/node-eval/blob/master/index.js but I have not yet figured out if and how.
Similar, but more related to require: Load node.js module from string in memory
https://github.com/floatdrop/require-from-string
did not work either
all the variants with node:vm like
import vm from 'node:vm'
let context = vm.createContext({}, {name:"temp1.mjs"})
vm.runInContext(js, context, {filename: "temp2.mjs"})
result in "Cannot use import statement outside a module"
The best way would be nodes vm.Module class
https://nodejs.org/api/vm.html#vm_module_evaluate_options
but that is in draft stage and behind feature flags.

What does a sub-path mean in the context of import statements?

Suppose I have a statement like this:
import * as bar from 'foo/bar';
I have a few questions:
What does the /bar bit mean?
Is there a sepcific (google-able) term for this?
Does this provide efficiency gains (reducing module bloat)?
How can I bundle a common-js module (ideally with Rollup) on NPM and support this syntax?
Breaking it down:
Firstly to provide context to your first and second question, let's go a bit further and break down all the parts of that statement:
import * as quux from 'foo/bar';
Note: The first bar instance in your example has been intentionally changed to quux to disambiguate the following points.
Collectively it is referred to as an Import Statement, or more specifically; an Import Declaration as defined in the ECMA-262 Imports Syntax Specification.
The import part is referred to as the Import Keyword.
The * as quux part is referred to as a NameSpace Import.
The asterisk * indicates that all exported bindings in bar will be imported to a newly created namespace object called quux. The resultant effect is that the quux object will hold all the exported members from bar.
The quux part of that is referred to as the Imported Binding and, as previously mentioned, it's used as the name for the newly created namespace object.
The from 'foo/bar' part is referred to as the From Clause.
The foo/bar part is a String Literal and is referred to as the Module Specifier... it's simply a filepath.
Answers to your specific questions:
What does the /bar bit mean?
It's part of the Module Specifier which is part of the From Clause. It is simply a reference to the name of the file whose exported member(s) will be imported from. In this example its exported member(s) will be imported into a new namespace object called quux.
With regards to what is that actual "part" called; ECMA-262 doesn't provide a specific reference/name for it, however, I refer to that part as the filename; (i.e. the filename in the filepath of the module to import).
As far as I know, /bar may or may not need to include a file extension (i.e. /bar.js). Again there's nothing in the ECMA-262 Imports Syntax that enforces this. ECMAScript only defines the syntax for modules and not the specific mechanism(s) for loading them. So the requirement for with or without a .js suffix may vary per implementation. For example:
In Apple's latest Safari browser built upon WebKit which runs on iOS the inclusion of the .js suffix is necessary.
In Modules docs for Node v8.10.0 they do include the .js file extension for imported files in their examples. Note: As ES6 Modules currently have a Stability Index 2 this could change.
Is there a specific (google-able) term for this?
Not per se, as ECMA hasn't named it specifically. You could try googling some of the terminology/nomenclature that I've previously mentioned.
Does this provide efficiency gains (reducing module bloat)?
I'm not sure I fully understand what you mean by "module bloat". However, If you mean will it result in less modules in application code? I think not, it's more likely result in an increased usage of modules.
Efficiency gains will be had from there being native ES6 Module support. Native support by it's very nature can bring speed/performance increases. Other benefits of ES6 Modules are:
ES6 Modules are statically analyzable which allows dead/unused code elimination via a process named Tree Shaking. More info here and here . So, if that's what you mean by the term "module bloat" - then yes; unused/dead code will be removed by tools that perform Tree Shaking.
Caveat; I'm not 100% certain whether tools that perform Tree Shaking are capable of removing dead code when using the wildcard/asterisk (*) in a NameSpace Import. It MAY be necessary to import specific member(s) that you'll use instead, i.e.
import { foo, bar, quux } from './foo/bar.js';
It's an official ECMAScript standard.
The example that you gave included a NameSpace Import, so there's another benefit - it will help to avoid namespace collisions.
More benefits are mentioned here.
How can I bundle a common-js module (ideally with Rollup) on NPM and support this syntax?
Rollup works with a plugin named #rollup/plugin-commonjs which will convert CommonJS modules to ES6, so they can be included in a Rollup bundle.

Is using an ES6 import to load specific names faster than importing a namespace?

I've found at least two ways to import functions in from a module like Ramda for example. There are probably a few more ways to do something very similar like const R = require('ramda');
Option 1 is to import certain functions:
import { cond, T, always, curry, compose } from 'ramda';
Option 2 is to import the whole module like:
import * as R from "ramda";
I would prefer to reference the module from which the function is being called like so:
R.T();
But if the 2nd option is used, does it bring in every Ramda function not just the ones used in a module I'm working in? Are there any impacts on actual memory use, or bandwidth use as far as what gets sent to the browser if option 2 is used?
Is it possible to somehow do this:
// invalid syntax below:
import R { cond, T, always, curry, compose } from 'ramda';
R.T();
My question is kinda related to this one, but it's a bit different
import R (ramda) into typescript .ts file
TL;DR: It does not matter.
import * as … from 'ramda';
import { … } from 'ramda';
will both by default always bring in the complete Ramda module with all its dependencies. All code inside the module would be run, and which syntax was used to reference the exported bindings doesn't matter. Whether you use named or namespaced imports comes down to preference entirely.
What can reduce the file size to download and the used memory is static analysis. After having evaluated the module, the engine can garbage-collect those bindings that are referenced from nowhere. Module namespace objects might make this slightly harder, as anyone with access to the object can access all exports. But still those objects are specified in a way (as immutable) to allow static analysis on their usage and if the only thing you're doing with them is property access with constant names, engines are expected to utilise this fact.
Any size optimisation involves guessing which parts of the module need to be evaluated and which not, and happens in your module bundler (like Rollup or WebPack). This is known as Tree Shaking, dropping parts of the code and entire dependencies when not needed (used by anything that got imported). It should be able to detect which imports you are using regardless of the import style, although it might have to bail out when are doing unusual things with the namespace object (like looping it or using dynamic property access).
To learn about the exact guesses your bundler can make, contact its documentation.
#Bergi is right in his comment, which I think should be the answer. I would also like to point out you can always try things out in Babel to see what it compiles to: click here to see what an example destructuring actually does
So basically even if you destructure just one function from the module, the whole module will be required. In the Babel example I gave, I just extracted Component from the 'react' module, but the compiled code actually just required the whole thing. :)
Adding to #Bergi, Also just for future reference if you want to shed unused functions and import only the desired function, use selective import like below
import isEmpty from 'ramda/src/isEmpty';
This way you can complement it with Webpack and get a better tree shaking. Hope it helps

Why are package imports needed in Meteor

About a year ago I have used Meteor, and now I want to use it again, but many things have changed.
When I follow the Blaze tutorial on Meteor.com, they add imports on top of their files:
import { Meteor } from 'meteor/meteor';
import { Template } from 'meteor/templating';
import { ReactiveDict } from 'meteor/reactive-dict';
I got the app working. But when I comment the imports out, the app keeps working like it should work. Why are these imports needed?
I am still using the regular Javascript, not ES6.
Thanks!
The import statement is used to import functions, objects or primitives that have been exported from an external module, another script, etc.
The name parameter is the name of the object that will receive the exported members. The member parameters specify individual members, while the name parameter imports all of them. name may also be a function if the module exports a single default parameter rather than a series of members. Below are examples to clarify the syntax.
Import an entire module's contents. This inserts myModule into the current scope, containing all the exported bindings from "my-module.js".
For more detail about the different ways we can use import along with their usage, please check this.
They still use the old globals for backwards compatibility. However it is recommended to use the imports so if in some future release they remove the globals your code will still work. You can read more in the appropriate section of the guide.
Ok you know import is to import an exported object from another file already.
The point that you may have missed is that MDG heard the need to stop loading everything by default, or at least to provide a mean to control what is loaded in memory and what is not.
Look for the /imports special directory.
Files in that folder are no longer loaded automatically, but only through import statement.
As for the tutorial, I guess they did not explained this functionality, and because it imports only standard functionalities which are still loaded eagerly for backward compatibility, it does not change anything removing those statements.

When using ES6 import statement, is there a way to protect against items being undefined?

import {
foobar1,
foobar2,
foobor3, //typo! this key doesn't exist in the module.
} from './module_file.js'
console.log(foobar1, foobar2, foobar3) //EXPLODES
One of the most frequent silly mistakes I make when using the new ES6 style import statement is that I'll have a typo in one of the keys in object destructuring. I can't think of a single instance where I'd ever want a value in a destructuring assignment to be undefined. Is there any way to force the import statement to fail-fast if one of the items I'm trying to import is undefined?
ie:
import {
doesntExistInModule //EXPLODE NOW! 🔥🔥🔥
} from './module_file.js'
There is no hook allowing a module to run some code before its dependencies load. This means that modules have no control over how
their dependencies are loaded.
There is no error recovery for import errors. An app may have hundreds of modules in it, and if anything fails to load or link,
nothing runs. You can’t import in a try/catch block. (The upside here
is that because the system is so static, webpack can detect those
errors for you at compile time.)
For more details, read it out
The module stuff in the spec is pretty gnarly, but I believe a real implementation will throw a SyntaxError at 15.2.1.16.4 ModuleDeclarationInstantiation( ) Concrete Method step 12.d.iii in that case. Since there are no legit implementations I don't know if you're talking about a way to do it in transpiled code in the meantime, or if you don't realize that's the case and will be satisfied to know it'll work that way eventually. There's been talk before of trying to implement that kind of check in Babel, but as far as I know nothing's actually been done to that effect. Babel compiles each module in isolation.
Also, this is not object destructuring, it just has similar syntax.

Categories

Resources