Import modules from folder-based npm dependency - node.js

My project contains three subprojects. The shared subproject is used by the other subprojects:
- myproject
- server
node_modules
package.json
- client
node_modules
package.json
- shared
node_modules
package.json the name property is "shared"
foo.ts
bar.ts
I want to reference the shared subproject. I don't like the npm link approach as it has drawbacks (package duplication). So I tried the npm install <folder> approach:
cd server
npm i ../shared
cd ..
cd client
npm i ../shared
That adds "shared": "file:../shared" to the two main project.json files. Good.
BUT... How do I reference modules from the common subproject? I tried:
import * as foo from "foo";
import * as foo from "shared/foo";
import shared from "shared";
// and other combinations...
I don't want to use "../../../../../shared/node_modules/shared/foo/bar/baz" as that defeats the purpose of this approach. What is the clean/proper way to do this?

Related

How to NPM publish so that consumers can imports from different top-level directories

I am publishing a library to NPM. The library was written in Typescript, and the result of the Typescript compilation is put in a lib/ folder. The main file is an index.ts that exports my public API:
export * from './functions';
export * from './models';
In package.json, I specify:
{"main": "lib/index.js", "types": "lib/index.d.ts", "files": ["lib/**/*"]}
Once my package is published and installed elsewhere, its folder structure is:
lib/
index.js
index.d.ts
functions/
index.ts
my-function.ts
models/
index.ts
my-model.ts
the consumer imports everything from the package root:
import {someFunction, SomeModel} from 'a-library';
This works, but I like the idea of segregating paths. One path for functions, and another for models. Currently if I remove the top-level index, the imports become segregated but they now include the lib/ folder in the path which I don't want:
import {someFunction} from 'a-library/lib/functions';
import {SomeModel} from 'a-library/lib/models';
What I would actually like to accomplish is the same but without lib/. I have a working solution but it seems too convoluted. Here is what works:
In package.json, remove the main types and files key.
Delete or empty the index.ts
To publish replace the simple npm publish with the following steps:
copy package.json and paste it into /lib folder
cd into the /lib folder and run npm pack and take note of the created tarball's name
run npm publish {{TARBALL_NAME}}
cd back up to the project root
As a result, the published package has the following folder structure once installed:
functions/
index.ts
my-function.ts
models/
index.ts
my-model.ts
Because things are no longer nested within a lib/ directory the consumer can import as I wished:
import {someFunction} from 'a-library/functions';
import {SomeModel} from 'a-library/models';
The problem is that packing and publishing to accomplish this seems too cumbersome. I'll never get my teammates to by into a 3 or 4 step process when they've just been running npm publish all this time.
Is there a simpler way to accomplish what I'm after?

Import local npm package using scope

My monorepo:
/app1
package.json
/app2
package.json
/shared
package.json
The shared/package.json has "name": "#company/shared".
The app projects' package.json files have dependecy "#company/shared": "file:../shared".
When referencing the shared code, I want a "short" style, which is also less liable to break when things are moved around:
import { foo } from "#company/shared"
But that doesn't work, so I have to do this::
import { foo } from "../../../../../shared/src/something"
I fiddled with both package.json and tsconfig.json without success.
How do I set that up?
You should link your shared package to your dependent packages using npm-link.
cd ~/shared # go into the package directory
npm link # creates global link
cd ~/app1 # go into some other package directory.
npm link #company/shared # link-install the package
this will tell npm to install the package from the shared folder, and update with any changes made to the original package
for more info see https://docs.npmjs.com/cli/link.html
EDIT:
I have realized only now that you are planning to upload the shared package to the server. in that case you may use the module-alias package, https://www.npmjs.com/package/module-alias
this will allow you to make imports such as const sharedModule = require('#shared/moduleName');
EDIT #2:
For typescript, use https://www.npmjs.com/package/tsconfig-paths
Actually there is a part missing from my code above.
The shared project needs to export the shared stuff in an index.js (i.e. a "barrel" file) and reference that in the package.json:
"main": "dist/index.js",
"types": "dist/index.d.ts",
And then the alias import style works.

Setting the "root" of a published npm project

I'm publishing an npm package named foo to the npm registry.
I wrote the package using a compile-to-js language.
For sanity, I put the compiled output into the dist/ folder of the project directory.
My package.json lists the entrypoint as dist/entry.js:
{
"name": "foo",
"main": "dist/entry.js",
}
Sometimes, I want to use files within the package that are not part of the entry point. For example, there is a very useful export called whatever inside of dist/util.js:
import { whatever } from "foo/dist/util";
This works, but forcing the users of my package to type dist/ in all import statements is inconvenient.
Furthermore, re-exporting every possible util function is not DRY. I do not want to re-export from the entrypoint.
Ideally, I would like to import files from dist/ using the following syntax:
import { whatever } from "foo/util"
How do I configure my package.json to search for files in the dist/ folder of my project?
This cannot be done.
This is the reason why some packages have entry point file that re-exports all public exports (not everything that resides in dist is intended to be used by end user), e.g. #angular/core.
And the reason why some packages have unsuitable file structure that is published to NPM registry and favours proper import paths, e.g. rxjs.

When using a private git url for an npm module, how can I configure the consuming application to only use files from the module's dist folder?

I am using angular-cli for my angular application, but because angular-cli currently does not support use for creating a library, I used the seemingly most widely used git project to create my library: https://github.com/jvandemo/generator-angular2-library
My issue is that I don't want to publish my npm module library to the public directory. Instead I want to use the git url directly in my dependencies. During development, this works fine. I can run the build locally and run an npm link inside the "dist" folder and everything is dandy. However when I push my npm module code to git, and then run an npm install in the consuming project, I'm not sure how to set it so that my consuming project just looks inside the dist folder of the module and treats it as if it was the root of the module.
For example, in node_modules/my_private_module, my file structure looks like:
dist/
-- component1
-- compoennt2
-- my_module.metadata.json
-- my_module.d.ts
-- my_module.umd.js
-- index.d.ts
-- index.js
-- package.json
-- README.MD
package.json
README.md
All the files that my application is using are in the /dist folder, but I DO NOT want to specify "dist" in all my imports like
import { myComponent1 } from 'my_private_module/dist';
I want to be able to just specify
import { myComponent } from 'my_private_module";
As I do in development when I run an npm link in the dist folder.
Is there a way I can achieve this?
Thanks
In package.json for your module, in the root folder:
typings: 'dist/index.d.ts',
main: 'dist/index.js'
Remove the package.json in your dist folder.
When the package is resolved from import {...} from 'my_private_module', the module loader will look for a folder called my_private_module under node_modules, and look either for index.js which defines the exports, or within package.json for the main property - which in your case also points to index.js from the dist folder.
It is good practice to put package.json where you want your module to be found, and have main and typings point to index.js and index.d.ts.
I answered a similar question here and it seems relevant.
Basically, treat the generated library in the dist folder as it's own repo. In order to keep the git init files and folders, you tell ng-packagr to not destroy the destination when building. Then you push the changes to the library specific repo and use that as your package url in other projects.

Requiring node modules locally

When working with modules already registered on NPM, the process of including them is easy: run npm install <package> and then add var package = require('<package>')
However, I'm not sure of the way to "set things up" when working on my own module. I'm not ready to publish to NPM but I do want to require the module in the same way as outlined before.
Therefore, I've completed the following steps:
Created a sub-directory inside the node_moduels directory for my module
Added a package.json file (via npm init) inside this new directory
Included a dependencies section in the package.json file
Is this the correct approach to using node modules locally.
Also, when I run npm install the dependencies do not appear to be detected in my module's package.json file - I assume this is an issue with the way I've gone about things?
I would not suggest putting it in the node_modules directory. This folder should be excluded from your source control.
Here's a minimal end to end example.
Put this file wherever you like. I suggest a 'lib' folder within your directory structure
myModule.js
module.exports = function(callback){
return callback("hello there");
};
Then, wherever you want to use it:
app.js
var myModule = require('./lib/myModule');
myModule.sayHello(function(hello) {
console.log(hello);
});
Now, if you run node app.js your console output will be:
hello there
As your myModule grows, you can refactor this into a separate set of files, create an package.json for it, and publish it to NPM
EDIT
Based on your comment, it looks like this is what you want
Local dependency in package.json
So, based on that, along with our above example, edit your package.json as follows
{
"dependencies": {
"myModule": "file:../lib/myModule"
}
}
Then you can require as:
var myModule = require('myModule');
If / when you publish myModule to npm, you can just change your package.json
ANOTHER EDIT
As another alternative, you can specify git urls in your package.json without publishing to NPM
Use Git dependencies with npm and Node on Heroku
This would be as easy as doing the following:
In the root directory of your (unpublished) module execute npm link
In the root directory of your module requiring that unpublished module execute npm link UNPUBLISHED_MODULE_NAME.
voilĂ !

Resources