using the ... spread syntax in javascript es6 named exports - javascript

I am attempting to import everything from a library as a hash, modify it, and re-export the modified hash, without knowing all of the named exports in a library. For example:
import * as reactBootstrap from 'react-bootstrap';
wrappedReactBootstrap = doFunnyThingsTo(reactBootstrap);
export {
...wrappedReactBootstrap
};
// or
export wrappedReactBootstrap;
My understanding of https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export is that the following is not allowed by the spec. Could someone confirm?
Obviously, looping is out of the question, since export and import statements must be top level.

Object rest spread is stage 3 proposal and not a part of any spec (will probably be included in ES2018).
More importantly, export has syntax that mimics existing JS syntax but doesn't interpret { ... } as an expression. export syntax was strictly defined because ES2015 modules are supposed to be statically analyzed. This is one of their benefits, but it requires the developer to specify exports and imports explicitly.
Since { ...wrappedReactBootstrap } introduces dynamic export (it was used here exactly for this purpose), it isn't supported by ES2015 module export and it is very unlikely that it will be.
If it is necessary to provide dynamic behaviour for the export, it can be exported and imported as named or default object.
import * as reactBootstrap from 'react-bootstrap';
export default doFunnyThingsTo(reactBootstrap);
And used like
import wrappedReactBootstrap from '...';
const { funny, thing } = wrappedReactBootstrap;
Obviously, wrappedReactBootstrap object won't get the benefits of ES2015 modules this way, e.g. tree-shaking.

Related

Importing CommonJS modules to an ESM syntax

I'm struggling to understand how to import CommonJS modules into an ESM syntax. I'm currently trying to work with the library url-metadata. url-metadata exposes a top-level export as a callable (which does not really conform to CommonJS, AFAIK):
const urlMetadata = require('url-metadata')
urlMetadata(URL, ...)
It's not possible to write:
import urlMetadata from 'urlMetadata'
since no default export is defined.
Instead, I have to write:
import * as urlMetadata from 'url-metadata'
Or:
import urlMetadata = require("url-metadata")
I tried to read up on module loading in Node but I'm still somewhat confused as to what is the correct way to do this and why.
import urlMetadata from 'url-metadata';
is syntactic sugar for
import { default as urlMetadata } from 'url-metadata';
Either will work fine.
The value assigned to module.exports in a CommonJS module is the default export.
See the Node.js docs.

Is there a use case for exporting the same const as a named and default export?

Question
I have seen a large codebase where every file with constants looks something like this:
export const DEFAULT_ID = 0;
export const CURRENT_CODE = 'ABC123';
export default {
DEFAULT_ID,
CURRENT_CODE
};
They are using both a named as well as a default export for all constants. When it comes to how the constants are actually being imported, it seems that they are usually simply importing them as named exports:
import {CURRENT_CODE} from './whatever.consts';
Is there any use case for this practice? It’s unusual, since, normally, either a named or a default export is used, not both.
Research
I checked this question hoping to get some insight into why one would use them together in this manner, but I couldn’t find anything.
The closest thing I got to an answer was in this article. In the section “Why expose a symbol as both default and named exports?”, they provide a brief example of how this allows someone to use import {A, B} from './a' instead of needing to write something like import A, {B} from './a'. However, this explanation doesn’t make sense to me since the same syntax can be used if the constants are simply exported as named exports.
My Thoughts
The only reason I can think of is that this approach can give more flexibility when it comes to importing constants. I.e., it allows using both
import {DEFAULT_ID, CURRENT_CODE} from './whatever.consts';
let id = DEFAULT_ID, code = CURRENT_CODE;
and
import INITIALIZATION_CONSTS from './whatever.consts';
let id = INITIALIZATION_CONSTS.DEFAULT_ID, code = INITIALIZATION_CONSTS.CURRENT_CODE
for importing the constants.
Is this a valid reason for using this approach? Are there any best practices implications?
Is there any use case for this practice?
Not really. The only thing I can think of is backwards-compatibility, possibly related to how they are transpiling their code, if the module is a library used elsewhere.
The only reason I can think of is that this approach can give more flexibility when it comes to importing constants.
A default export is not necessary for that. You can easily use a namespace import with named exports only:
import * as INITIALIZATION_CONSTS from './whatever.consts';
let id = INITIALIZATION_CONSTS.DEFAULT_ID, code = INITIALIZATION_CONSTS.CURRENT_CODE

When do we use typescript import * as?

Trying to developer a mental model for what import * as Blah does. For example:
import * as StackTrace from 'stacktrace-js';
How does that work and when do we do import *?
Not really an answer, but a usage: Consider you have a few constant strings to be used in your application, you can define them in a single file and export
export const name = "NAME";
export const someOtherString = "SOME_CONST_STRING";
Then you can import them in a single variable using:
import * as CONST_STRINGS from './yourFilePath';
and use as
CONST_STRINGS.name
CONST_STRINGS.someOtherString
From the TypeScript doc:
Import the entire module into a single variable, and use it to access the module exports
The example code imports all exports of the stacktrace-js module into a variable called StackTrace.
Any named exports will be available as properties of the same name.
If the module has a default export it will be available as the default property.
Note also from the TypeScript Module doc:
Starting with ECMAScript 2015, JavaScript has a concept of modules. TypeScript shares this concept.
So TypeScript modules behave in the same way as ES6 JavaScript modules.
You would use import * as in either TypeScript or JavaScript when you want access to all of the module exports in a single variable.

Does importing the default export along with a named export load the module twice?

What happens when you write an import statement in the following form:
import React, { Component } from 'react';
Is destructuring of the import module occurring as in destructuring an object to achieve Component instead of needing React.Component? Or is it importing with a named export with completely differing syntax, though it does resemble destructuring?
An important corollary question: does import React, { Component } ... needlessly load up the Component object from the React module twice as compared to simply import React ... (given that Component is a constituent of the larger React library)?
To answer your first question:
No, it's not object destructuring. The syntax may have been set up that way to relate but there's no confirmation that they were intentionally made to be related. Per the ECMAScript 2015 Language Specification:
Section 15.2.2 Imports
Syntax
[...]
ImportClause :
[...]
ImportedDefaultBinding , NamedImports
[...]
NamedImports :
{ }
{ ImportsList }
{ ImportsList , }
It's completely separate syntax.
To answer your second question:
Yes, it imports it twice, once React for access as React.Component by the default export, and once as Component as a named export. Per the specification:
Section 12.2.2 Static Semantics: BoundNames
[...]
ImportClause : ImportedDefaultBinding , NamedImports
Let names be the BoundNames of ImportedDefaultBinding.
Append to names the elements of the BoundNames of NamedImports.
Return names.
As you can see, the names you import with import React, { Component } are bound twice, meaning you get React as the default export and thus React.Component, and then the bound name Component is also appended to your imported names. You essentially get it twice under two different bindings or names.
It should be noted that only the bound names are different. React.Component and Component refer the same object, just with different bindings because you imported using named exports. Once you import React, React.Component has already been imported. All { Component } does is create a new binding to the already imported object.
There is no destructuring happening in the import syntax. Even though it looks somewhat similar - it's a separated syntax.
The imported identifiers are bindings to the objects created during module initialisation. So practically you get 2 bindings to the same object, which costs you 1 extra reference, nothing more.
No matter how many times in your source code tree you import a module it would only be initialised once, with all the values created just once. And all the import statements would essentially "bind" to the values in memory without creating duplicates.

How do i write this require as an es6 import statement

Problem
I have this require statement
require('smoothscroll-polyfill').polyfill();
But I would like to write it as an es6 import statement
I have tried
import 'smoothscroll-polyfill';
and
import * as 'smoothscrollPolyfill' from 'smoothscroll-polyfill';
But cant get it to work correctly, so what is the correct way to import a package like this?
You'd do it in two parts, first the import, then the function call:
If polyfill itself is a named export and it doesn't care what this is when it's called:
import {polyfill} from 'smoothscroll-polyfill';
polyfill();
(And you've now confirmed that was the case.)
For completeness, before the above was confirmed, I also listed these other possibilities which may be useful for others in future:
If polyfill is a property on the default export (rather than its own named export), then we import the default (no {} in the import statement) and then use its property:
import smoothScrollPolyFill from 'smoothscroll-polyfill';
const polyfill = smoothScrollPolyFill.polyfill();
If the smoothScrollPolyFill part is a named export and polyfill is a property on it, then we'd use the {} on import:
import {smoothScrollPolyFill} from 'smoothscroll-polyfill';
const polyfill = smoothScrollPolyFill.polyfill();
using import {} from '...' instead.
import {polyfill} from 'smoothscroll-polyfill';//ref polyfill from 'smotthscroll-polyfill'
polyfill();//ref a method,so you must call it after imported.
Assuming you're using Babel or something similar to provide compatibility between CommonJS modules and ES6 modules, the syntax would look something like this:
import smoothscrollPolyfill from "smoothscroll-polyfill";
smoothscrollPolyfill.polyfill();

Categories