deploy package to npm with toplevel import - npm

I am trying to publish a package to npm but having trouble achieving the desired usage.
My project builds the files in a dist folder and when I do an npm publish it "works" but in order to use it I have to do:
import Something from 'package/dist';
But I want to be able to just import from the package itself like:
import Something from 'package';
In my package.json I have the following config:
{
"source": "src/index.js",
"main": "dist/index.cjs",
"module": "dist/index.mjs",
"scripts": {
"watch": "parcel watch",
"build": "parcel build",
}
}
I have tried copying the package.json into the dist folder after building and running npm publish from the dist folder but then the source is wrong (should be just index.js and not src/index.js) but if I have just index.js it will not build. It seems like most packages let you import from the top level directly (for example you can import React from 'react';)
I don't know what else to include in this question but I am happy to update with more info if required.

Add this to package.json
"exports": {
".": "./dist/index.js",
"./*": "./dist/*.js"
}
The exports field allows you to redefine paths to point to files or directories.

Related

pnpm, workspace dependency and also supporting publishing?

I am new to pnpm workspaces and am trying to resolve the following issue.
My demo project:
root
packages
common-ui
main-lib
common-ui is a Vite based package containing some Vue components that can be reused by other packages, in my example it's being used by main-lib.
"dependencies": {
"ui-common": "workspace:*"
},
common-ui is referencing an index.ts inside its package.json
"main": "index.ts",
index.ts is exporting my Vue components:
...
export { default as Heading } from './components/Heading/Heading.vue';
...
Now I am able to import those components inside main-lib:
import { Heading } from 'common-ui'
This all works fine but I would also like to be able to publish my library to the npm registry. As common-ui is using the Vite, it's possible to build in library mode: https://vitejs.dev/guide/build.html#library-mode. My package inside common-ui will need to change to:
{
"name": "common-ui",
"files": ["dist"],
"main": "./dist/common-ui.umd.js",
"module": "./dist/common-ui.es.js",
"exports": {
".": {
"import": "./dist/common-ui.es.js",
"require": "./dist/common-ui.umd.js"
}
}
}
main is not referencing index.ts anymore but a dist folder that only gets updated when the vite command is ran. Is there a way for me to support both publishing/versioning and referencing the actual source code from inside main-lib?
I've taken a quick look at Rush.js but I am not sure if provides a solution and I want to be sure before I continue on that path.

Bootstrap-vue components not imported when importing an npm package from local

Okay so maybe this has a fairly simple explanation which I don't know how to look up, but here's my conundrum:
if I publish my project (my-navigation) to the npm registry and then npm install it in another project (my-vue-app), it works all great, but!
if I try to npm install my-navigation directly from its folder on my machine into my-vue-app, I start getting runtime errors indicating that I have not correctly registered some bootstrap-vue components
I have even tried copying the files under node_modules/my-navigation into a folder and then npm installing that - I get the same errors
This is my main entrypoint:
import Vue from "vue";
import MyNavigation from "./MyNav.vue";
import {
BNavbar,
BNavbarBrand,
BNavbarNav,
BDropdownForm
} from "bootstrap-vue";
Vue.component("b-navbar", BNavbar);
Vue.component("b-navbar-brand", BNavbarBrand);
Vue.component("b-navbar-nav", BNavbarNav);
Vue.component("b-dropdown-form", BDropdownForm);
Vue.component("b-form-radio", BFormRadio);
import "./styles/bootstrap/mystyles.scss";
export default {
install(Vue) {
Vue.component('my-navigation', MyNavigation);
},
};
export { MyNavigation };
and in package.json:
"main": "./dist/my-navigation.umd.js",
"module": "./dist/my-navigation.esm.js",
"unpkg": "./dist/my-navigation.min.js",
"files": [
"dist/*"
],
"dependencies": {
"core-js": "^3.3.2",
"vue": "^2.6.10"
},
"peerDependencies": {
"bootstrap-vue": "^2.0.4"
},
"scripts": {
"build-bundle": "vue-cli-service build --target lib --name my-navigation ./src/main-navbar.js"
},
I can of course work around this by importing the components directly in MyNavigation.vue, but I want to register them globally for use in another component I'll be including in the npm package as well; and well it just seems weird to me that it works through the registry but not locally
Edit: it appears that through the registry, the bootstrap-vue components are being registered globally and are available then in my-vue-app by importing the npm package. This seems like a bad idea(?), so I probably don't want that anyway.
npm pack produces a .tgz file https://docs.npmjs.com/cli/pack.html
Importing from this file instead of from dist has the same behaviour as importing from a package on the registry.
Still not sure why or what npm does in creating this file, but that answers at least the question of how to mimic the behaviour of a registered package when importing from local/a repository.

What are phantomChildren in package.json?

When I install a module, a list of phantomChildren appears in the package.json file. What are phantomChildren?
I didn't find official documentation for npm package phantomChildren. But encountered some other explanation: https://rushjs.io/pages/advanced/phantom_deps/. It is about rast, but explains behavior of npm dependencies pretty well.
For example library A might import definitions from libraries B and C, but then B and C can both import from D, which creates a “diamond dependency” between these four packages.
A “phantom dependency” occurs when a project uses a package that is
not defined in its package.json file.
Some live example:
my-library/package.json
{
"name": "my-library",
"version": "1.0.0",
"main": "lib/index.js",
"dependencies": {
"minimatch": "^3.0.4"
},
"devDependencies": {
"rimraf": "^2.6.2"
}
}
my-library/lib/index.js
var minimatch = require("minimatch")
var expand = require("brace-expansion"); // ???
var glob = require("glob") // ???
Wait a sec – two of these libraries are not declared as dependencies
in the package.json file. How is this working at all!? It turns out
that brace-expansion is a dependency of minimatch, and glob is a
dependency of rimraf. During installation, NPM has flattened their
folders to be under my-library/node_modules. The NodeJS require()
function finds them there because it probes for folders without
considering the package.json files at all.
To summarize: if package uses dependencies of it's own dependencies, it can be treated as phantomChildren. Package doesn't have such dependencies directly but uses it from other places.

Running Mocha 6 ES6 tests with Babel 7, how to set up?

For a library written in ES6/7, I want to compile (to ES5) the library to a dist/ folder. I also want to run the tests (written in ES6/7) for this lib.
My dev dependencies look like this (package.json):
"devDependencies": {
"#babel/cli": "^7.4.4",
"#babel/core": "^7.4.5",
"#babel/preset-env": "^7.4.5",
"#babel/register": "^7.4.4",
"chai": "^4.2.0",
"mocha": "^6.1.4",
"sinon": "^7.3.2"
},
My build and test scripts looks like this (package.json):
"scripts": {
"test": "mocha --require #babel/register",
"build": "babel src -d dist --presets=#babel/preset-env"
},
Running npm run build works well. The dist/ folder gets populated with transpiled files.
Running npm run test does not seem to work - this is my problem.
> mocha --require #babel/register
/Users/dro/Repos/lib/node_modules/yargs/yargs.js:1163
else throw err
^
ReferenceError: regeneratorRuntime is not defined
Initially I got an import error, which was resolved by adding .babelrc file.
Below is my .babelrc file content.
{
"presets": ["#babel/preset-env"]
}
I was reading about regeneratorRuntime and it got me to this link about babel-polyfill where they explain I shouldn't need that polyfill.
This will emulate a full ES2015+ environment (no < Stage 4 proposals) and is intended to be used in an application rather than a library/tool.
What is needed to set this up properly?
I am not using webpack.
Testing in ES6 with Mocha and Babel 7. Look here: https://dev.to/bnorbertjs/my-nodejs-setup-mocha--chai-babel7-es6-43ei or http://jamesknelson.com/testing-in-es6-with-mocha-and-babel-6/
npm install --save #babel/runtime
npm install --save-dev #babel/plugin-transform-runtime
And, in .babelrc, add:
{
"presets": ["#babel/preset-env"],
"plugins": [
["#babel/transform-runtime"]
]
}
Look at the project documentation:
npm install --save-dev babel-register
In your package.json file make the following changes:
{
"scripts": {
"test": "mocha --require babel-register"
}
}
Some features will require a polyfill:
npm install --save-dev babel-polyfill
{
"scripts": {
"test": "mocha --require babel-polyfill --require babel-register"
}
}
Below steps are for applying Babel transformations & core-js polyfills for your tests file:
💡 All transformations are only done per current environment, so only what is needed to be transpiled/polyfilled, will be. Target environments may be defined from a .browserslist file or as a property in package.json file. (read more here)
Step 1: Install packages:
#babel/core (read why)
#babel/preset-env (read why)
#babel/register (read why)
core-js (read why)
Note that #babel/polyfill exists and uses core-js under the hood. However, it was deprecated in favor of using core-js directly.
Step 2: Create a Babel configuration file babel.config.js
(used to be .babelrc.js or a .json file).
Create this file at the root-level of your code.
The most basic configuration (for just testing and not bundling) would look like this:
module.exports = {
presets: [
['#babel/preset-env', {
"corejs": "3.26",
"useBuiltIns": "usage"
}],
};
corejs - This is the polyfills library and should be specified with the minor version, otherwise x.0 will be used.
It is needed when testing code on rather "old" Node versions, which do not support all of the language methods. This ofc depends on your own usage of such javascript methods. (for example String.prototype.replaceAll).
useBuiltIns - must be set in order for the corejs polyfills to be applied. Read about it in the official docs.
By default, #babel/preset-env will compile your code for the current environment, but you can specify a different environment by setting the "targets" option in the configuration.
Ofc, you can add more presets like #babel/preset-react for example, if your code it written in React, or any other plugins which are specifically needed for your code.
Step 3: Connect mocha to the babel configuration:
In your package.json file
Under the scripts section, simply write something like this:
"test": "mocha \"src/**/*.test.js\""
Create a .mocharc.json file with this content:
{
"exit": true,
"color": true,
"require": ["#babel/register"],
"ignore": "node_modules"
}
This will apply Babel transformations to all of your test files.
If you need need to apply some special global javascript before/to all of your tests, you can add another file to the require setting, for example, fixtures.cjs:
"require": ["#babel/register", "fixtures.cjs"],
fixtures.cjs:
Below example applies a chai (popular alongside Mocha) plugin for testing DOM-related code:
var chai = require('chai'),
chaiDOM = require('chai-dom');
// https://stackoverflow.com/questions/62255953/chai-usechaihttp-once-or-in-every-test-file
// https://mochajs.org/#global-teardown-fixtures
exports.mochaGlobalSetup = function () {
chai.use(chaiDOM);
}
Interesting reads:
Babel vs babel-core vs babel-runtime
How does mocha / babel transpile my test code on the fly?

.npmignore ignored when installing local module

All our server projects contain a git submodule folder (let's say modules), which contains our custom modules/components.
Such module dependencies are installed locally (see serverApp/package.json) so that we don't have to include the whole submodule folder to the final rpm. What I'm having trouble with is limiting the number of files included in node_modules.
The submodule structure looks like the following:
modules
|--loader
|--dist => compiled js files here that are created when installing the module
|--ts => contains typescript files that shouldn't be included in node_modules
|--package.json
|--tsconfig.json
|--more modules
|--.gitignore
Adding an .npmignore file inside modules/loader doesn't seem to help as the whole folder is copied.
modules/loader/tsconfig.json:
{
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"declaration": true,
"outDir": "./dist",
"strict": true
}
}
modules/loader/package.json:
{
"name": "loader",
"version": "1.2.0",
"private": true,
"description": "",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"preinstall": "npm run build",
"build": "../../node_modules/typescript/bin/tsc",
"test": "echo \"Error: no test specified\" && exit 1"
},
"dependencies": {
"#types/lodash": "^3.9.3",
"#types/nomnom": "0.0.28",
"#types/yamljs": "^0.2.30",
"lodash": "^3.9.3",
"nomnom": "^1.8.1",
"yamljs": "^0.2.1"
},
"devDependencies": {
"typescript": "~2.3.4"
}
}
serverApp/package.json:
{
"name": "my-server-app",
"version": "2.3.0",
"description": "",
"main": "myServerApp.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"license": "private",
"dependencies": {
"loader": "file:modules/loader"
},
"devDependencies": {
"grunt": "^0.4.5",
"grunt-cli": "^0.1.13"
}
}
I'm not sure if it has to do with the fact that we have a .gitignore file or because the module is not published and installed locally.
npm version => 5.3.0
EDIT
Doesn't work with specifying the "files" in modules/loader/package.json either
As after checkout the issue I found out below useful points which need to mention out :
We use a .npmignore file to keep stuff out of your package.
If there's no .npmignore file, but there is a .gitignore file, then npm will ignore the stuff matched by the .gitignore file.
If you want to include something that is excluded by your .gitignore file, you can create an empty .npmignore file to override it.
Like git, npm looks for .npmignore and .gitignore files in all subdirectories of your package, not only the root directory.
Similar to .gitignore file .npmignore also follow these rules
Blank lines or lines starting with # are ignored & Standard glob patterns work.
You can end patterns with a forward slash / to specify a directory.
You can negate a pattern by starting it with an exclamation point !.
By default, the following paths and files are ignored, so there's no need to add them to .npmignore explicitly
Additionally, everything in node_modules is ignored, except for bundled dependencies. npm automatically handles this for you, so don't bother adding node_modules to .npmignore.
Testing whether your .npmignore or files config works
If you want to double check that your package will include only the files you intend it to when published, you can run the npm pack command locally which will generate a tarball in the working directory, the same way it does for publishing.
And you can also checkout same issue here Consider methodologies for reducing the number of files within node_modules #14872
Thanks.
Did you checked with node 0.6.13 / npm 1.1.9? This issue is common in npm 1.1.4 .
have a look on this link
You mentioned that you do not want to include "whole submodule" into the "final rpm", by which I presume the package you will finally prepare. I reproduced similar setup and added a '.npmignore' to ignore "submodule" which I installed using npm install --save ./task_in where 'task_in' was my module kept along side of the main package's('task') 'package.json'.
And when the final package was prepared using npm pack while being in the 'task' folder, I got a package(a tar file) without the folder('task_in') as indicated in the '.npmignore'.
While working, though, I found that the module 'task_in' folder was copied to 'node_modules' which is automatically not included in the final package( refer here). Also, while the package is prepared, ".gitignore" is over-ridden by ".npmignore".
So, this is my "two cents" and I hope it helps you.