Can I find a package's peer dependencies on www.npmjs.com - npm

I have a project that has around 50 packages installed through NPM. After several months of neglect I now need to update most packages, resulting in several mismatching peer dependencies. In order to find the correct combination of package versions I want to see the peer dependencies of all versions of a certain package.
Where on www.npmjs.com can I find a package's peer dependencies?
A package's page shows "dependencies" and "dependents", but I believe these are normal dependencies, not "peer dependencies".

The www.npmjs.com website doesn't expose peer dependencies information. However, this metadata resides at the npm registry endpoint: https://registry.npmjs.org/.
Via your command line you can access it by utilizing the npm view command. For instance:
npm view <pkg_name> peerDependencies
Note: You'll need to substitute the <pkg_name> part with a real package name.
The aforementioned command will list peerDependencies for the latest version of the given package.
I want to see the peer dependencies of all versions of a certain package.
To achieve this you can:
Run the following command to obtain all versions for a given package
npm view babel-loader versions --json
Note Here we check for babel-loader but this could be any valid package name
This will print:
[
"4.0.0",
"4.1.0",
"4.2.0",
"4.3.0",
"5.0.0",
...
]
Then run the following command to obtain the peer dependencies for each version previously listed:
npm view babel-loader#4.0.0 peerDependencies --json
^^^^^^
prints:
{
"babel-core": "^4.0.0",
"webpack": "^1.4.5"
}
Repeat again - similar to before but change the #<version> suffix, i.e. #4.1.0 in the example below.
npm view babel-loader#4.1.0 peerDependencies --json
^^^^^^
This prints:
{
"babel-core": "^4.7.0",
"webpack": "^1.4.5"
}
and so on...
Automate the task:
You may want to consider automating the steps above by creating a node.js script as follows. This utilizes the nodejs execSync() method to shell out the necessary npm commands, however you could change it to utilize the exec() method if you want it to run asynchronously:
script.js
const fs = require('fs');
const sh = require('child_process').execSync;
const PKG_NAME = 'babel-loader'; // <-- Change the package name.
const destFilePath = PKG_NAME.replace(/\//g, '_') + '-peer-deps.json';
const versions = JSON.parse(sh('npm view ' + PKG_NAME + ' versions --json').toString());
const data = versions.map(function(semver) {
const pkgVersion = PKG_NAME + '#' + semver;
console.log('> Fetching peerDependencies info for: ' + pkgVersion);
const peerDeps = sh('npm view ' + pkgVersion +
' peerDependencies --json').toString().replace(/[\r\n]/, '');
return {
name: pkgVersion,
peerDependencies: peerDeps ? JSON.parse(peerDeps) : null
}
});
fs.writeFileSync(destFilePath, JSON.stringify(data, null, 2));
console.log('\nDone !');
Then run the following command to invoke the node.js script:
node ./path/to/script.js
Note: You'll need to redefine the ./path/to/ part as necessary.
Given the value of babel-loader currently assigned to the PKG_NAME variable in script.js you'll see something like the following logged to your console:
> Fetching peerDependencies info for: babel-loader#4.0.0
> Fetching peerDependencies info for: babel-loader#4.1.0
> Fetching peerDependencies info for: babel-loader#4.2.0
...
Upon completion it will write a .json file to disk named babel-loader-peer-deps.json, which includes the following content:
babel-loader-peer-deps.json
[
{
"name": "babel-loader#4.0.0",
"peerDependencies": {
"babel-core": "^4.0.0",
"webpack": "^1.4.5"
}
},
{
"name": "babel-loader#4.1.0",
"peerDependencies": {
"babel-core": "^4.7.0",
"webpack": "^1.4.5"
}
},
{
"name": "babel-loader#4.2.0",
"peerDependencies": {
"babel-core": "^4.7.0",
"webpack": "^1.4.5"
}
},
...
]
EDIT: Reducing the number of https GET requests
If you're wanting to reduce the number of https GET requests to just one then I suggest utilizing the nodejs builtin https.get() to fetch JSON data from the https://registry.npmjs.org/ endpoint.
This example gist below will be much faster.
get-peer-deps.js
const fs = require('fs');
const path = require('path');
const https = require('https');
const pkgName = process.argv[2];
const myName = path.basename(__filename);
// Check the package name has been provided
if (!pkgName) {
console.log('\x1b[40;37m%s\x1b[0m \x1b[40;31m%s\x1b[0m', myName,
'ERR!', 'Missing package name argument');
process.exit(1);
}
const fileName = pkgName.replace(/\//g, '_') + '-peer-deps.json';
const destFilePath = path.join(path.dirname(__filename), fileName);
const endPoint = 'https://registry.npmjs.org/' + encodeURIComponent(pkgName);
// Request JSON from npm registry endpoint.
https.get(endPoint, function(resuest) {
console.log('> Fetching peerDependencies info for: %s...', pkgName);
var response = '';
resuest.on('data', function(chunk) {
response += chunk;
});
resuest.on('end', function() {
processJsonResponse(response);
});
}).on('error', function(err) {
console.error(err);
});
/**
* Processes the JSON response to extract the necessary metadata and saves
* resultant JSOn to disk.
* #param {String} data - The JSON response from the npm registry.
*/
function processJsonResponse(data) {
const versions = JSON.parse(data).versions;
const semvers = Object.keys(versions);
const peerDepsInfo = semvers.map(function(semver) {
const current = versions[semver];
return {
name: current.name + '#' + current.version,
peerDependencies: current.peerDependencies || null
};
});
fs.writeFile(destFilePath, JSON.stringify(peerDepsInfo, null, 2), function(err) {
if(err) {
return console.log(err);
}
console.log('> Done. Saved result to:\n %s', destFilePath);
});
}
Usage:
Then run the following command via your command line tool to invoke get-peer-deps.js:
node ./path/to/get-peer-deps.js babel-loader
^^^^^^^^^^^^
Note: When invoking get-peer-deps.js it's necessary to provide the package name as an argument. In the example above we pass in babel-loader. This can be replaced with whichever valid package name you prefer. For example in the next example we pass in #angular/forms:
node ./path/to/get-peer-deps.js #angular/forms
^^^^^^^^^^^^^^
The resultant .json file will be formatted as described previously and will be save to the same directory where get-peer-deps.js resides.

Related

Add packages to custom #frontend_components or lib folder using yarns

When migrated from bower to yarn and ran the command
yarn install
yarn created #bower_components folder and all the bower/front-end components were add inside this folder ../node_modules/#bower_components
"dependencies": {
...
"#bower_components/bootstrap": "twbs/bootstrap#^3.3.7",
"#bower_components/jquery": "jquery/jquery-dist#^3.1.1",
"#bower_components/lodash": "lodash/lodash#^4.17.12",
"#bower_components/moment": "moment/moment#^2.19.1",
... }
If, I need to create migrate from #bower_components to #frontend_components or add to public/lib folder. How do I do it?
yarn --cwd /public/lib add lodash
The way bower-away works is by creating a symlink from /bower_components to /node_modules/#bower_componets:
bower-away gets away with this by resolving all dependencies with
Bower, and adding all of them flattened to package.json
Then, your package.json file with contain bower dependencies as:
{
"dependencies": {
"#bower_components/bootstrap": "twbs/bootstrap#^3.3.7",
"#bower_components/jquery": "jquery/jquery-dist#^3.1.1",
"#bower_components/lodash": "lodash/lodash#^4.17.12",
"#bower_components/moment": "moment/moment#^2.19.1",
}
}
This will work in most scenarios. But in your case, the symlink is not working. I ran the same issue and my fix was to modify my express server and map /bower_components front-end resource into /node_modules/#bower_components backend resource with the following line:
New
app.use("/bower_components", express.static(path.join(__dirname, "/node_modules/#bower_components")));
Original
//app.use("/bower_components", express.static(path.join(__dirname, "/bower_components")));
If this is not your case, you may need to manually update front-end references to new node_modules/#bower_components folder just as advised as the original author: Adam Stankiewicz
But initially, the only change required in code is to change any
reference to bower_components with node_modules/#bower_components
(though you can link it somewhere else in postinstall script).
The workaround which I implemented to resolve this issue was by introducing a simple script and updating the package.json with a new key.
Under package.json (for all the UI related dependencies needed to work with the front-end)
...
...
"scripts": {
"postinstall": "node migrateUI.js"
},
...
"uidependencies": {
...
"bootstrap": "^3.3.7"
"jquery": "^3.1.1",
"lodash": "*",
"moment": "^2.19.1",
...
},
"dependencies": {
....
"bootstrap": "^3.3.7"
"jquery": "^3.1.1",
"lodash": "*",
"moment": "^2.19.1",
....
}
migrateUI.js
const uipackage = require('./package.json');
const packageName = Object.keys(uipackage.uidependencies);
const dir = 'public/libs';
//if the folder already exists, it ignores else creates.
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir);
}
for (let i = 0; i < packageName.length; i++) {
const element = packageName[i];
const source = path.resolve('node_modules/' + element);
const target = path.resolve('public/libs/' + element); //custom lib folder to save all UI dependencies
if (!fs.existsSync(target)) {
fs.symlinkSync(source, target, 'dir', (err) => {
if (err && err.code !== 'EEXIST') {
console.log('Error creating dependecny symlink - ', err);
} else {
console.log('Symlink for dependency created');
}
});
}
}

How to use npm package dependency

I am learning to create npm packages by creating a session check function sessionFn that will popup a modal 1 minute before the session expires.
The function works as expected on the main app (a nuxtJS app) but when I make use it as an npm module I have to pass moment as an argument even though moment is listed as a dependency and is imported on the module.
I am missing something, why is my module not picking up moment? I want to use the module like sessionFn(this, to.path); instead of sessionFn(this, to.path, moment); moment is undefined when I don't pass it
Package files
package.json
{
"name": "hello-stratech",
"version": "1.0.17",
"description": "Hello Stratech",
"main": "index.js",
"keywords": [
"npm",
"hello",
"stratech"
],
"author": "Simo Mafuxwana",
"license": "ISC",
"dependencies": {
"moment": "^2.22.2"
}
}
index.js (main js file)
import moment from "moment";
module.exports = {
greeting(name) {
alert("Hello.. " + name);
},
department(dev) {
...
},
sessionFn(context) {
const exp = context.$store.state.session.exp;
let userSystemTime = new Date();
userSystemTime = moment.utc(userSystemTime)
const diff = moment(userSystemTime).diff(moment(exp), 'minutes');
if (diff = 1) {
// open modal
}
}
}
Usage
This is how I use the package in the main app
import moment from 'moment';
import { sessionFn } from "hello-stratech";
export default {
...
watch: {
$route(to) {
sessionFn(this, to.path, moment);
}
}
...
}
You dont need to import and pass moment into your function since you are importing it in your function file. You are not even using the passed argument in your function. So you can just safely dont pass it. You are also not using second argument that u pass to.path, so u can omit it too.
As a suggestion you should install and use eslint, which will catch such things. You can setup a nuxt project with eslint using create nuxt app for example.
There is also a bug in esm 3.21-3.22 that prevent commonjs and es6 imports work together https://github.com/standard-things/esm/issues/773 . That should be fixed when a new esm will be released
Try making moment as devDependancy through npm i moment --save-dev instead of dependency.
That way moment will only be needed when the package is development (means when you are developing the project) but not when it is used.
Hope it fixes your issue
for more depth knowledge

Use stream-browserify with expo

stream cannot be used with expo, as it is a Node.js standard package. However, the package stream-browserify can be used as an alternative in those scenarios.
In order to make modules resolve this instead of the native Node package, I am trying to make babel-plugin-require-rewrite work with expo.
I am adding this to babel.config.js:
module.exports = function(api) {
api.cache(true);
return {
presets: ['babel-preset-expo'],
plugins: [
["rewrite-require", { aliases: {
"stream": "stream-browserify"
}}]
]
};
};
Unfortunately, it is not respected by the bundler. I get this error when trying:
The package at "node_modules\qr-image\lib\qr.js" attempted to import the Node standard library module "stream". It failed because React Native does not include the Node standard library. Read more at https://docs.expo.io/versions/latest/introduction/faq.html#can-i-use-nodejs-packages-with-expo
Is it possible to make this work in Expo?
You dont need to modify babel config to use stream-browserify in your source. You can import stream-browserify in your App.js. I have created a simple example on GitHub.
App.js
const Stream = require('stream-browserify');
Package.json
"dependencies": {
"buffer": "^5.2.1",
"events": "^3.0.0",
"stream-browserify": "^2.0.2",
"readable-stream": {
"version": "2.3.6",
"dependencies": {
"core-util-is": "github:mjmasn/core-util-is"
}
}
...
}
stream-browserify has dependency readable-stream which has its own dependency and use node environment. To resolve it you have to add these node packages. You can read about core-util-is fork here.
This answer rn-nodeify install that i have posted should work. Except Step 1 & Step 5 follow all steps. Step 3 is used for adding node packages you are specifically looking to install, in this case specify stream. Please do modifications in Step 4 based on your requirement in Step 3.
Please do comment if you want me to elaborate.
What ended up working for me was creating a metro.config.js file with the following content (I used readable-stream instead of stream-browserify, but I think either should work):
module.exports = {
resolver: {
extraNodeModules: {
stream: require.resolve('readable-stream'),
},
},
};
And then I just used yarn add readable-stream and this allows dependencies to use readable-stream as if it were stream.
This was based on the info I found here: https://gist.github.com/parshap/e3063d9bf6058041b34b26b7166fd6bd#file-node-modules-in-react-native-md

React Native : RelayQL: Unexpected invocation at runtime

I am getting the error:
RelayQL: Unexpected invocation at runtime. Either the Babel transform
was not set up, or it failed to identify this call site. Make sure it
is being used verbatim as Relay.QL
I have the following packages with versions:
"babel-preset-es2015": "^6.14.0",
"babel-relay-plugin": "^0.9.2",
"react-native": "^0.30.0",
"react-relay": "^0.9.2",
"babel-preset-react-native": "^1.9.0"
My babelrc looks like this:
{
"passPerPreset": true,
"presets": [
{
"plugins": [
"./schema/babel-myrelay-plugin.js"
]
},
"react-native",
"es2015"
]
}
My babel-myrelay-plugin.js looks like this:
// `babel-relay-plugin` returns a function for creating plugin instances
const getBabelRelayPlugin = require('babel-relay-plugin');
// load previously saved schema data (see "Schema JSON" below)
const schemaData = require('./schema.json');
// create a plugin instance
const plugin = getBabelRelayPlugin(schemaData.data);
module.exports = plugin
This was working earlier, till I renamed(mv) the main directory of the project. I did a rm -rf node_modules and re-installed. I performed a npm cache clean. I also cleaned the $TMPDIR.
But the error is persistent.
I am able to query/mutate using GraphiQL.
Any help appreciated.
PS: I have also researched the issues logged on relay. The solutions listed there didn't help.

Pulling files from a directory into the root folder for NPM

I am publishing a library to NPM.
When I build the library, the resulting artifact is placed in the dist folder located in the root of my project as index.js.
When users install from NPM I would like index.js to be present in the root of the folder created in their node_modules folder. Presently, it remains in a directory named dist.
How can I do this?
My packages.json:
{
"name": "my-package",
"version": "0.0.9",
"files": ["dist/*"],
"main": "index.min.js",
"private": false,
"dependencies": {},
"devDependencies": {},
"repository": "git#github.com:username/my-package.git"
}
I had exactly the same problem.
I solved it not by copying the files up, but by copying the files I needed down into the ./dist/ folder and then doing an npm publish from there; NPM then treats that folder as a complete package and everything works very nicely. The only files I needed to copy from the root folder were:
package.json
README.md
Because we're going to copy these files down into the ./dist/ folder before we do the publish, we do NOT want the package.json file to reference ./dist/. So remove the package.json's files entry completely, because we don't need to tell it which files we'll take - we're going to take everything in the ./dist/ folder. I'm using TypeScript so I also have a typings entry, and again no reference to ./dist/.
{
"name": "my-package",
"version": "0.0.9",
"main": "index.min.js",
"typings": "index.d.ts",
"private": false,
"dependencies": {},
"devDependencies": {},
"repository": "git#github.com:username/my-package.git"
}
Now for the publish step. I built a gulp task that will perform the publish for me, making it nice and automated (except for incrementing the package version #).
From gulp I'll use Node's spawn() to kick-off the npm process. However, because I'm actually working on Windows I used "cross-spawn" rather than the normal built-in Node.js spawn (which I learned the hard way didn't work when I had spaces in my path!).
Here's my gulp file, with the TypeScript bits removed:
var gulp = require('gulp');
var del = require('del');
var spawn = require('cross-spawn'); // WAS: require('child_process').spawn;
var config = {
src: { tsFiles: './src/**/*.ts' },
out: { path: './dist/' }
}
gulp.task('clean', () => {
return del('dist/*');
});
gulp.task('build', ['clean'], () => {
....
});
gulp.task('publish', ['build'], (done) => {
// Copy the files we'll need to publish
// We just use built-in gulp commands to do the copy
gulp.src(['package.json', 'README.md']).pipe(gulp.dest(config.out.path));
// We'll start the npm process in the dist directory
var outPath = config.out.path.replace(/(\.)|(\/)/gm,'');
var distDir = __dirname + '\\' + outPath + '\\';
console.log("dist directory = " + distDir);
// Start the npm process
spawn('npm', ['publish'], { stdio:'inherit', cwd:distDir } )
.on('close', done)
.on('error', function(error) {
console.error(' Underlying spawn error: ' + error);
throw error;
});
});
Notice when we call spawn() we pass in a 3rd argument which is the options. The main entry here is the cwd:distDir, which tells spawn to run the npm process from the ./dist/ directory. Because using spawn can cause problems I've hooked into the spawn error handling. As I was troubleshooting my use of spawn() I found the following StackOverflow article very helpful.
This worked like a charm; my published package has all the files in the root directory and the ./dist/ folder is not published.