What are some ways to deal with es6 modules and dependencies?
In particular I have 1 library i'd like to split into 3 modules. Let's call the library "salad" and uses "utensils" and "ingredients". Right now all 3 parts are part of the same library but I'd like to separate them. "utensils" is useful on its own. "ingredients" should be separate if only because it only includes a few common ingredients. It's perfectly valid to use the "salad" library with your own ingredients.
So, in actually trying to do it I run into issues. Utensils is easy, it has no dependencies
// utensils.js
export { fork, knive, spoon, }
salad is also easy, it has no dependences
// salad.js
export { chop, toss, dice }
but ingredients is where the issues show up
// ingredients.js
import { knive } from './utensils.js'; // what path here?
import { chop, dice } from './salad.js'; // what path here?
export { choppedLettuce, dicedTomates } // they use 'chop' and 'dice'
If everything is local and in my own project this is not so difficult but I want all 3 projects to be publically available and easily usable.
Use cases include
using with rollup/webpack
using live in the browser no build
using via npm with rollup/webpack
using in node no build
If I use npm based paths (assuming the packages are named 'utentils' and 'salad'
import { knive } from 'utensils';
import { chop, dice } from 'salad';
Then ingredients.js is no longer usable without npm and pacakge.json. No more live import in a browser
If I use local paths
import { knive } from './utensils.js';
import { chop, dice } from './salad.js';
It only works if I put all the files in the same folder which kind of defeats the purpose of separating them.
In other languages I'd probably end up adding include paths or some other way for ingredients.js be able to reference paths that are resolved later. I think webpack has some #symbol solution but that's webpack specific.
One other solution which seemed less than desirable was to have a setup function in ingredients.js to pass in the dependencies
// ingredients.js
export { setup, choppedLettuce, dicedTomates }
which would then be used like
import { knive } from './utensils.js';
import { chop, dice } from './salad.js';
import { setup, choppedLettuce, dicedTomates } from 'ingredients.js';
// pass in the deps so no paths needs to be ingredients.js
setup({knive, chop, dice});
That seems like a non-scalable solution.
It could also be that every function in ingredients takes needs to be passed salad and utensils. That seems yuck.
Is there a solution? Do I just need to have multiple versions of the libraries, some for npm based inclusion, others for live inclusion? Am I overlooking some obvious solution?
Related
I am setting up my module aliases in a NextJS project and everything runs fine so far.
The issue I have comes when I have a folder utils that includes both, an index.ts but also another someModule.ts.
My imports looked like this before:
import { someHelper } from '../utils'
import { anotherHelper } from '../utils/someModule'
Assuming I am setting the following module alias "#/utils/*": ["utils/*"], I would need to amend the first of the two imports like this:
import { someHelper } from '#/utils/index' // <-- see how I need to add index
unless I am extending the tsconfig with a second module alias like this: "#/utils": ["utils"].
This feels a bit like duplication to me, so I am wondering whether this is needed or do you have another option how to solve this?
What is the best way to elegantly export all interfaces / enums in a directory?
Case
Take the following:
import { TimeFrame } from '#types/shared/TimeFrame'
import { Address } from '#types/shared/Address'
import { Contact } from '#types/shared/Contact'
import { ApiBaseParameters } from '#types/api/BaseParams'
import { MailApiQueryParameters } from '#types/api/MailApiQueryParameters'
import { MailApiBodyParameters } from '#types/api/MailApiBodyParameters'
import { MailApiResponse } from '#types/api/MailApiResponse'
import ...
import { StateEnums } from '#enums/States'
import { CountryEnums } from '#enums/CountryEnums'
Can we make it something easier to use? For example...
import * as Type from '#types'
import * as Enum from '#enums'
or
import {
TimeFrame, Address, Contact,
ApiBaseParameters,
MailApiQueryParameters, MailApiBodyParameters, MailApiResponse,
...
} from '#types'
import { StateEnums, CountryEnums } from '#enums'
Each .ts file contains something like
interface TimeFrame {
start_time: string
end_time: string
}
export { TimeFrame }
Issue
Our interfaces are growing quickly and it's taking too much time to track down what is included and where to find all the interfaces / enums.
Eventually, this may become a packaging and we want it so that when another developer installs the package, they can easily see all the interfaces included.
I know it would be possible to add in each directory an index.ts file with export * from './fileName but then you have to hope all contributors remember to do this for every file.
Question
Is there a better approach to this? Is it possible to automatically or programatically create an index.ts file that exports all exports in the directory to...
Slim the import statements in other files.
Reduce effort of others writing their own interface when it already exists
Maybe get some autofill options like Type.Ma.... and then see all the types beginning with "Ma"?
There are libraries that generates index.ts files automatically. And allows you to configure them. Like Barrelsby, Create-ts-index.
Create-ts-index also has a library that allows you to configure it programmatically.
What you can do with them is add a post build step. Use their CLI or API to generate index.ts files.
Reduce effort of others writing their own interface when it already
exists
Maybe get some autofill options like Type.Ma.... and then see
all the types beginning with "Ma"?
For those you may seek for IDE plugins. Some jetBrains producst (Webstorm, IntelliJ and Resharper for Visual Studio) already does these kind of things.
For suggestion part Visual Studio Code has Auto Import plugin. When you type a Type that suggests you among existing types and when you select auto imports it. And there a bunch of plugins similar to it.
Current cssuseragent package (2.1.31) doesn't export anything. There is only one variable named cssua. I want import/require it into my project with webpack.
I have tried to adding export keyword before cssua variable and it worked. But this is not a good solution. If anybody else upgrade the package in future, she/he won't know that s/he must do this.
In CLI:
npm i cssuseragent
Then I export cssua variable:
//'export' was not exist, I added it
export var cssua = (
//some code here
)(/*some arguments here*/)
Then I can import:
import { cssua } from 'cssuseragent';
Is there any way to say "If you resolve to this file in import/require, get it as 'custom-name' with its all content" to webpack with a loader or plugin? Because changing source code of a 3rd party module is not a good way. Also it may not be as easy as this every time, the module can be huge. And I want a generic way to do this, maybe like adding just the path of module.
I don't know of a webpack plugin that does that, but you can
// customExport.js
import { cssua } from 'cssuseragent';
export default { cssua };
in another file
// other_file,js
import customExport from "customExport.js";
I'm trying to create a build pipeline which doesn't bundle the files together, but instead uses <script type="module">. This will let me just recompile files as they change without rebundling, greatly improving build times during development.
Our project uses ES6, so this is generally easy.
There is however one snag: third-party modules that only have CommonJS builds (such as react).
There are a few ways around this. For now, I have a transform that changes the import name from react to /node_modules/react and my server is smart enough to then go find the appropriate dist file from node_modules and serve it up. This all works fine.
The problem is that it gets confused when I try to do something like:
import { Component } from 'react';
That won't work how it currently is (because it gets confused by there not being a default). However, this will work:
import * as React from 'react';
const { Component } = React;
I could manually do this for all files and packages, but a) that would make it unnecessarily ugly (with Redux and other things, there are half a dozen different packages in many files we'd have to do this to and b) there are lots of files, I don't want to manually change them all.
Is there a Babel transform plugin that can automatically make this kind of conversion? It seems that this isn't a completely novel approach, so I'm hoping there is a plugin that'll do it for me that my Google-fu failed to find.
I managed to get this working. I ended up having to write my own Babel plugin which makes the changes that I was looking for. Basically, for the different versions of the import statement, it changes it around in order to work better with UMD and CJS modules.
For example, something like this:
import A, { a, b, c } from 'a';
was transformed into something like this:
import * from __babel_A from 'a';
const A = __babel_A.default && (__babel_A.default.default || __babel_A.default) || __babel_A;
const { a, b, c } = __babel_A.default || __babel_A;
This format came about based on the way most things export. Many (like React) would put everything into an object with it's name on either module.exports (if I provided a fake one) or this or window. Others ended up not grouping things together (like ReactRTE) and had its own "default", which is where the default.default bit came from.
In addition to this transform, when my server serves up the dist versions of the third-party files, it'd wrap them up in a way that would then let me do an export default. That looked like this:
const module = {};
const keys = Object.keys(this || window);
const toExport = (function __auto_wrapper() {
${fileCode}
return module.exports || Object.keys(this).reduce((r, k) => keys.includes(k) ? r : Object.assign(r, { [k]: this[k].default || this[k] }), {});
}).call(this || window);
export default Object.keys(toExport).length === 1 ? Object.entries(toExport)[0][1] : toExport;
Again, the different ways are based on what the different projects output. Some will use module.exports when given it, others won't consider module.exports a real thing since I (purposely) didn't initialize export as an object (if I did, it'd try to use require() which I didn't want). In my case, ReactRTE had just a CJS module instead of an UMD, so I also had to do replace on its code to replace require('react') and require('react-dom') with references to the objects on window instead.
This all works as I wanted (completely unbundled code loading in the browser). The only slight side-effect is that React and friends are all available on window (which they normally wouldn't be if bundled properly), but that's pretty minor.
With this code
const noCurrencies: Map<CurrencyCode, Currency> = new Map();
/**
* Reducer for the finished currency request.
*/
export function currencies(state: Map<CurrencyCode, Currency> = noCurrencies, action: Action): Map<CurrencyCode, Currency> {
switch (action.type) {
case SET_CURRENCIES:
return action.currencies;
default:
console.log('currencies(reducer): noCurrencies', noCurrencies)
return state;
}
}
I get this console output:
currencies(reducer): noCurrencies undefined
Is this a known problem with Babel? How do I debug it? I have a feeling that it's due to this particular file having been called twice during initialisation and thus having a circular dependency with another file.
(I'm not 'recreating a repro from scratch', so don't suggest that, and the types are https://github.com/flowtype/flow-typed which get removed in a pre-processor step, and I've tried without types as well, with the same result)
The reason was a large circular dependency between modules. Even if using ECMAScript module syntax, the compiler (WebPack 3 + babel) is not smart enough to only load what is needed when it's needed; but instead evaluate all exports and imports whenever a file is used/touched.
That means that if you have index.js files that liberally export things from around them, to create a single entry-point for callers outside its folder, you'll likely run into issues like this.
I had to go through the code-base and make the imports 'deep' by directing imports straight to the, of the index file, surrounding files, instead.
E.g.
folder A
epics.js
commands.js
index.js
types.js
actions.js
reducers.js
messages.js
...
folder B
import { create } from '../A' becomes import { create } from '../A/types'
and so forth for all things; in the end I'm only exporting React views and types from index.js