Import local npm package using scope - node.js

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.

Related

How do I load a package from a package in a Lerna monorepo?

I have:
packages
-models
-package.json
-....
-server
-src
-index.ts
-package.json
In my packages/server/package.json, I have:
"scripts": {
"dev": "ts-node src/index.ts"
},
"dependencies": {
"#myapp/models": "../models",
In my packages/server/src/index.ts, I have:
import { sequelize } from '#myapp/models'
In my packages/models/src/index.ts, I have:
export type UserAttributes = userAttr
export { sequelize } from './sequelize';
but it gives me an error:
Try `npm install #types/myapp__models` if it exists or add a new declaration (.d.ts) file containing `declare module '#myapp/models';`
import { sequelize } from '#myapp/models'
How do I get this to work properly?
Lerna will take care of the dependencies between your local packages, you just need to make sure you set them up correctly. The first thing I would suggest is to go to #myapp/models and make sure that your package.json contains the fields you will need: main and more importantly types (or typings if you prefer):
// packages/models/package.json
{
// ...
"main": "dist/index.js",
"types": "dist/index.d.ts",
// ...
}
As you can see I made both of them point to some dist folder, which takes me to my second point - you will need to build every package as if it was a separate NPM module outside of the monorepo. I am not saying you need the dist folder, where you build it is up to you, you just need to make sure that from the outside your #myapp/models exposes main and types and that these are valid and existing .js and .d.ts files.
Now for the last piece of the puzzle - you need to declare your #myapp/models dependency as if it was a "real" package - you need to specify its version rather than point to a folder:
// packages/server/package.json
{
"dependencies": {
// ...
"#myapp/models": "0.0.1" // Put the actual version from packages/models/package.json here
// ...
}
}
Lerna will notice that this is a local package and will install & link it for you.
I don't know Lerna, but a good tool to deal with monorepos is npm link.
cd packages/models
npm link
cd packages/server
restore the version in dependencies "#myapp/models": "x.y.z",
npm link #myapp/models
It should be enough.
Hope this helps.
lerna bootstrap first then yarn add <package_name> works
Or
lerna bootstrap first, then add the package and the target version then run yarn.
After you put in the dependencies in your server's package.json, you just need to run
lerna run --stream build
And your local should be able to access to it.

Import modules from folder-based npm dependency

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?

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.

Get version number from package.json in React Redux (create-react-app)

OP EDIT: If anyone else comes across this: the app was created using create-react-app, which limits importing to within the src folder. However if you upgrade react-scripts to v1.0.11 it does let you access package.json.
I'm trying to get the version number from package.json in my app.
I've already tried these suggestions, but none of them have worked as I can't access package.json from outside the src folder (might be due to React, I'm new to this). Moving package.json into src then means I can't run npm install, npm version minor, and npm run build from my root folder. I've tried using process.env.npm_package_version but that results in undefined.
I'm using Jenkins, and I haven't set it up to push the commits up yet, but the only idea I have is to get the version from the tags in GitLab, but I have no idea how to do that, and it would add unnecessary dependency to the repo, so I would really like to find an alternative.
EDIT:
My file structure is like:
--> RootAppFolder
|--> build
|--> node_modules
|--> public
|--> src
|--> Components
|--> Root.js
|
|--> package.json
So to access package.json from Root.js I have to do import packageJson from './../../package.json' and then I get the following error:
./src/components/Root.js
Module not found: You attempted to import
./../../package.json which falls outside of the project src/
directory. Relative imports outside of src/ are not supported. You can
either move it inside src/, or add a symlink to it from project's
node_modules/.
Solving this without importing and exposing package.json to the create-react-app
Requires: version 1.1.0+ of create-react-app
.env
REACT_APP_VERSION=$npm_package_version
REACT_APP_NAME=$npm_package_name
index.js
console.log(`${process.env.REACT_APP_NAME} ${process.env.REACT_APP_VERSION}`)
Note: the version (and many other npm config params) can be accessed
Note 2: changes to the .env file will be picked only after you restart the development server
From your edit I would suggest to try:
import packageJson from '/package.json';
You could also try to create a symlink:
# From the project root.
cd src; ln -s ../package.json package.alias.json
List contents of src directory and you'll see the symlink.
ls
#=> package.alias.json -> ../package.json
Adding the .alias helps reduce the "magic" for others and your future self when looking at this. Plus, it'll help text editors keep them apart. You'll thank me later. Just make sure you update your JS code to import from ./package.alias.json instead of ./package.json.
Also, please take a look at this question:
The create-react-app imports restriction outside of src directory
Try this.
// in package.json
"version": "1.0.0"
// in index.js
import packageJson from '../package.json';
console.log(packageJson.version); // "1.0.0"
I don't think getting version by 'import' or 'require' package is correct.
You can add a script in you package.json
"start": "REACT_APP_VERSION=$npm_package_version react-app-script start",
You can get it by "process.env.REACT_APP_VERSION" in any js files.
It also works in build scripts, like this:
"build": "REACT_APP_VERSION=$npm_package_version react-app-script build",
import package.json
Generally speaking, importing package.json is not good. Reasons: security & bundle size concerns
Yes, latest webpack (default config) + ES6 import does tree-shaking (i.e. only includes the "version" value instead of the whole package.json) for both import packageJson from '../package.json' and import { version } from '../package.json'. But it is not guaranteed if you use CommonJS (require()), or have altered your webpack config, or use another bundler/transpiler. It's weird to rely on bundler's tree-shaking to hide your sensitive data. If you insist on importing package.json but do not want the whole package.json exposed, you may want to add some post-build checks to ensure other values in package.json are removed.
However the security concern here remains theoretical for open source projects whose package.json is public after all. If both security and bundle size are not problems, or, the non-guaranteed tree-shaking is good enough for you, then go ahead)
.env
The .env method, if it works, then it's good, but if you don't use create-react-app, you might need to install dotenv and do some additional configurations. There's also one small concern: it is not recommended to commit the .env file (here and here), but if you do the .env method, it looks like you will have to commit the file as it likely becomes essential for your program to work.
Best practice (arguably)
(this is not primarily for create-react-app, but you still can either use react-app-rewired or eject cra in order to configure webpack in cra)
If you use webpack, then with DefinePlugin:
plugins: [
new webpack.DefinePlugin({
'process.env.VERSION': JSON.stringify(
process.env.npm_package_version,
),
}),
]
You can now use console.log(process.env.VERSION) in your front-end program (development or production).
(You could simply use VERSION instead of process.env.VERSION, but it usually requires additional configuration to satisfy linters: add globals: {VERSION: 'readonly'} in .eslintrc (doc); add declare var VERSION: string; in .d.ts file for TypeScript)
Although it's "npm_package_version", it works with yarn too. Here's a list of npm's exposed environment variables.
Other bundlers may have similar plugins, for example, #rollup/plugin-replace.
Open your package.json file and change this line
Problem Is:
// in package.json
"scripts": {
"dev": "REACT_APP_VERSION=local REACT_APP_VERSION_NUMBER=$npm_package_version react-scripts start",
...
}
Solution is
// in package.json
"scripts": {
"dev": "react-scripts start",
...
}

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.

Resources