In NPM, is it possible to import all the modules in a scope?
say I have a scope in my artifact
#myorg/m1
#myorg/m2
#myorg/m3
....
#myorg/mn
My current app depends on all the packages in #myorg. Is there an option or way to say in my package.json that I want to import all of them together?
Having an index.js file in #myorg will work? Before I walk down that path, wanted to check around.
Thanks
Related
(Code below is a simple example, real scenario is bigger)
I have two modules, mod1.js and mod2.js, which are bundled together (using esbuild). They share a common dependency, util.js.
Problem is: when code in mod2.js imports util.js (using same alias), there's a conflict with names.
util.js:
export class Util {
...
}
mod1.js:
import Util from "./util.js";
...
mod2.js:
/* this raises an error of variable already declared */
import Util from "./util.js";
...
If I change alias in mod2.js, error goes away, as expected. But changing aliases every time I import util.js is a bit clumsy, and makes me think there has to be another way.
Is there a better approach to point to a common dependency from multiple modules which are bundled together?
Thanks in advance!
With help from #Bergi's comment, I figured out that I was not using esbuild to bundle my files, but rather using Hugo to concatenate them, and passing this to esbuild.
This leads to mentioned error because there are multiple imports in the same file, which esbuild correctly doesn't recognize as valid. Instead, using esbuild to bundle my files gives me correct results.
I'm still using Hugo, but I have a single entry point which it consumes, that imports all my scripts. From example, I have another file, say master.js:
master.js:
import mod1 from "./mod1.js";
import mod2 from "./mod2.js";
And I then pass this master.js to Hugo, using its js.Build function, which internally uses esbuild. This way I can import util.js using same alias, because these imports are in separate files, using ES6 linking from esbuild.
I'm developing a module that doesn't have a build that the user imports. Instead, he imports individual components and then bundles them along with his code. However, those components share utilities and I want to import them without going through relative path hell.
I know that's a pretty common question and I did some research. Suppose I have module/components/foo/bar/baz/index.js that wants to import module/utils/helper.js
Option 1
Just use relative paths and do:
import helper from '../../../../utils/helper'
Option 2
Use the module-alias package and have:
import helper from '#utils/helper'
This would work in Node.js because modules are resolved at runtime. However, let's say the module user has Webpack and imports the module:
import component from 'module/components/foo/bar/baz'
Webpack wouldn't be able to resolve #utils unless the user specifies that alias in his own Webpack configuration. That would be pretty annoying.
Option 3
Use Webpack aliases in webpack.config.js:
module.exports = {
resolve: {
alias: {
'#utils': path.join(__dirname, 'utils')
}
}
}
This would work fine if the module was pre-bundled. But as I've previously mentioned, I want the library to be usable with ES6 imports so that users can bundle only the parts they need.
Option 4
I could use the module name in the module's own source code:
import helper from 'module/utils/helper'
This appears to solve the problem, but I think it's a pretty bad solution. For development, you'd have to create a symlink node_modules/module -> module. I'm sure this hides many potential issues and collaborators would have to manually do it as well.
Is there a way to avoid relative paths while allowing the library to be used with ES6 imports?
I'm working with React Native and using index.js to manage modules. I have many projects consuming from the same components folder, which has a structure like this:
components
|_ComponentOne.js
|_ComponentTwo.js
|_index.js
In which the index.js looks like this:
export * from './ComponentOne.js';
export * from './ComponentTwo.js';
Now lets say I have three projects:
ProjectOne, which uses ComponentOne;
ProjectTwo, which uses ComponentTwo;
ProjectThree, which uses both;
Every project has its own files, but they refer to this same folder to use components (like a shared assets folder). Everything works fine while all the projects have all dependencies for all components.
In another words, I have a problem when one of the projects doesn't install a dependency for one of the components, even when the project doesn't uses that component.
Let's take as an example ProjectOne, which uses only ComponentOne. If ComponentTwo (which is not used in this project) has dependency X, I have to npm install dependency X even on ProjectOne, or an error is given. Again, ProjectOne doesn't use dependency X.
I can only image this happens because the index.js validates all declared exports, even when they're not used.
I'm trying to find an alternative to not be forced to install plugins and other things that I won't even use in my projects. I know that if I remove the index.js and start importing files directly on projects, it will work, but I would like to keep the index.js structure (to be able to use multi import syntax import { ComponentOne, ComponentTwo } from 'components').
Any suggestion?
Update:
The error I get, when I do not npm install dependency X is
Module `X` does not exist in the Haste module map
If I install it, everything works.
I'm using the terminal to install the application directly into an android phone. The JS bundle is automatically created by Metro (RN default).
I have a few npm modules published, all modules for existing libraries, like three.js or react.
The packages seem to be downloaded but i've received no feedback on whether it's done right or not.
Dependencies
What is the high level goal when defining dependencies?
three.js:
This is confusing because every "extension" just assumes that there is a THREE object available in some context.
My three.js module thus only mentions:
"devDependencies": {
"three": "^0.88.0"
}
And it's being used like this:
require( 'three-instanced-mesh' )(THREE)
Which both makes sense and doesn't.
The module can't work without three.js and a proper context passed in (THREE), but since i pass it in at runtime(?) it doesn't seem like it is an actual dependency. When I checkout the repo and want to develop in it, i do need to install three.js if i want the code to run.
React
I've published a React component which i intended to be used as such:
npm install my-module
import MyModule from 'my-module
<MyModule/>
For some reason I listed react as a peerDependencies dep.
<MyModule/> in JSX would imply that I've done something to have react already available in this context (similar to how THREE is passed in first example?).
The difference here is that i don't define the class at runtime, and thus calling import MyModule requires react to be available in MyModule.js?
What is the desired goal here and how to describe it? I only know that i don't want npm install my-module to install a different version of react, or to cause more react somehow to be bundled in the final bundle (but i'm not even sure about that).
What type of dependency (if any) should react be to my-react-component and how would i actually link it to my module?
For example using the externals thing with webpack vs having an actual import React from 'react'?
Build
If i set up my repo to work with the latest and the greatest of JS (or not even JS?), how and what should i publish?
import Foo from 'foo' //<-- where does 'foo' point and what is 'foo'?
What is the high level goal when defining dependencies?
You have to define which dependencies you are using only when developing (devDependencies) and the ones that are needed when someone installs your package and are going to be installed automatic (dependencies), and dependencies you need to be available, but you want the user to install (which, honestly, does not makes sense) peerDependencies.
The difference here is that i don't define the class at runtime, and thus calling import MyModule requires react to be available in MyModule.js?
It would require React to be available where the file is being imported, i.e: A imports myModule, but A has to have react imported. Putting as peer dependencies is the best way here indeed.
For example using the externals thing with webpack vs having an actual import React from 'react'?
Using externals in webpack just tells webpack to not bundle react and says that react will have been imported before the import of this component.
If i set up my repo to work with the latest and the greatest of JS (or not even JS?), how and what should i publish?
Usually the index.js file that contains the library minified/bundled. Publish that with npm, you'll need to setup main field on package.json
import Foo from 'foo' //<-- where does 'foo' point and what is 'foo'?
foo points to the name of the package that you created, i.e: the name this package was publish under. When you go to npmjs.org and search for foo, that is going to be the package. foo is in your node_modules.
Right now I pull in all my own es6 modules and create a bundle using Rollup.
Recently I started using VueJS, which now has an ES6 Module which can be pulled in just like my own modules. Rollup does some treeshaking on it, but I don't know if that is a very good idea? I don't know what it is doing, so I would rather it does nothing!
Instead I just add vue at the end of my HTML:
<script src="dist/bundle.js"></script>
I love the convenience of having everything as one bundled file, but should I really treeshake the entire Vue app, is there a command in Rollup that I can not treeshake just this one module?
EDIT
I have found the --external option, which seems good as it would just keep the import for vue and bundle the rest, but it does not seem to work!
When I use rollup --format=iife --external=../node_modules/vue/dist/vue.esm.browser.js --file=dist/bundle.js -- src/main.js it says Error: Could not resolve '../node_modules/vue/dist/vue.esm.browser.js' from src/app.js.
In my main.js it has import Vue from '../node_modules/vue/dist/vue.esm.browser.js; which works fine for the app. I want to make Vue an external, but it won't work!
To prevent Rollup from treeshaking a particular module, you can simply import it blindly (instead of a part of it), so that Rollup thinks the module performs some side effect:
import 'vue'
Of course you can still import some bits in parallel, so that you can rename the default export for example:
import 'vue'
import Vue from 'vue'
As for your --external option, you probably just need to wrap the path value with quotes:
--external='../node_modules/vue/dist/vue.esm.browser.js'
Note that you should probably switch to Rollup configuration file (instead of CLI options) to make your life easier. You will also be able to use rollup plugins, e.g. rollup-plugin-alias to manage the exact location of the Vue file you want to use.