Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add fix to properly handle versions with ~ and locked versions #242

Merged
merged 40 commits into from Dec 13, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
1cfa731
Add fix to properly handle versions with ~ and locked versions
jennyEckstein Nov 10, 2021
fc59855
Fix skipped unit tests
jennyEckstein Nov 10, 2021
47c4a6f
Remove debug logs
jennyEckstein Nov 10, 2021
b931e29
Attempt at refactoring module export
jennyEckstein Nov 11, 2021
c7af116
All unit tests refactored and pass
jennyEckstein Nov 15, 2021
3c3fa37
Uninstall chance
jennyEckstein Nov 15, 2021
b5ce7d9
Remove extra spacing
jennyEckstein Nov 15, 2021
3db1cc5
Remove TODO
jennyEckstein Nov 15, 2021
658c43f
Update package-lock
jennyEckstein Nov 15, 2021
b8090fa
Remove debug log
jennyEckstein Nov 15, 2021
653ac82
Test ndoe 16, 17 only
jennyEckstein Nov 15, 2021
ef90aa1
Put back node 14
jennyEckstein Nov 15, 2021
f5e5b48
Workaround to fix failing virtual mocks on windows
jennyEckstein Nov 15, 2021
a6e4eaf
Workaround to fix failing virtual mocks on windows
jennyEckstein Nov 15, 2021
9a35936
Workaround to fix failing virtual mocks on windows
jennyEckstein Nov 15, 2021
b4cc5d8
Workaround to fix failing virtual mocks on windows
jennyEckstein Nov 15, 2021
6690169
Test path structure
jennyEckstein Nov 16, 2021
5b3c48e
Test path structure
jennyEckstein Nov 16, 2021
a69a47e
Test path structure
jennyEckstein Nov 16, 2021
00898cf
Test path structure
jennyEckstein Nov 16, 2021
e08c7e3
Test path structure
jennyEckstein Nov 16, 2021
b3af94f
Temporarely remove coverage report
jennyEckstein Nov 16, 2021
4e1c194
Refactor unit tests to avoid virtual path mocking
jennyEckstein Nov 17, 2021
782eb1f
Move test helpers outside of lib
jennyEckstein Nov 17, 2021
007052f
Remove redundant directory name variables
jennyEckstein Nov 17, 2021
3c92420
Update package-lock
jennyEckstein Nov 17, 2021
8c8ed33
Put back report coverage
jennyEckstein Nov 17, 2021
30149df
Remove debug comment
jennyEckstein Nov 17, 2021
b9f3791
Refactored external commands out of index.js into utils
jennyEckstein Nov 24, 2021
64b95fd
Add coverage exception for util files
jennyEckstein Nov 29, 2021
322ace1
Fix typo in filename
jennyEckstein Nov 29, 2021
9da2413
Add documentation
jennyEckstein Nov 29, 2021
788b681
Fix types
jennyEckstein Nov 29, 2021
b672f89
Remove debug logs
jennyEckstein Nov 29, 2021
dd2e98d
Updated README
jennyEckstein Nov 29, 2021
0177181
Add missing description for object parameter
jennyEckstein Dec 6, 2021
314f738
Remove redandant async
jennyEckstein Dec 6, 2021
6fdbe7b
Fix error documentation for util files
jennyEckstein Dec 6, 2021
5814b9a
Update package-lock
jennyEckstein Dec 7, 2021
7e41f3b
Refactor to exclude only command execution
jennyEckstein Dec 9, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
15 changes: 14 additions & 1 deletion README.md
Expand Up @@ -38,6 +38,7 @@ verifyDeps({ dir: './path-to/project-directory' })
* [~getInstalledVersion(currentDir, name, logger)](#module_lifion-verify-deps--verifyDeps..getInstalledVersion) ⇒ <code>string</code> \| <code>null</code>
* [~pushPkgs(params)](#module_lifion-verify-deps--verifyDeps..pushPkgs) ⇒ <code>Array.&lt;Promise.&lt;PackageStatus&gt;&gt;</code>
* [~getPkgIds(filteredPkgs)](#module_lifion-verify-deps--verifyDeps..getPkgIds) ⇒ <code>string</code>
* [~removeLockedDependencies(deps)](#module_lifion-verify-deps--verifyDeps..removeLockedDependencies) ⇒ <code>Object.&lt;string, string&gt;</code> \| <code>Object</code>

<a name="exp_module_lifion-verify-deps--verifyDeps"></a>

Expand Down Expand Up @@ -146,7 +147,7 @@ Builds list of packages to update.
| Param | Type | Description |
| --- | --- | --- |
| params | <code>Object</code> | Object with parameters. |
| params.deps | <code>\*</code> | List of dependencies. |
| params.deps | <code>Object.&lt;string, string&gt;</code> | List of dependencies. |
| params.dir | <code>string</code> | Directory location. |
| params.logger | [<code>Logger</code>](#Logger) | Logging tool. |
| params.type | <code>string</code> | Type of dependency. |
Expand All @@ -163,6 +164,18 @@ Formats package name for installation.
| --- | --- | --- |
| filteredPkgs | [<code>Array.&lt;PackageStatus&gt;</code>](#PackageStatus) | Package properties. |

<a name="module_lifion-verify-deps--verifyDeps..removeLockedDependencies"></a>

#### verifyDeps~removeLockedDependencies(deps) ⇒ <code>Object.&lt;string, string&gt;</code> \| <code>Object</code>
Filters out dependencies with locked versions.

**Kind**: inner method of [<code>verifyDeps</code>](#exp_module_lifion-verify-deps--verifyDeps)
**Returns**: <code>Object.&lt;string, string&gt;</code> \| <code>Object</code> - List of dependencies excluding locked semver versions.

| Param | Type | Description |
| --- | --- | --- |
| deps | <code>Object.&lt;string, string&gt;</code> | List of dependencies. |


## License

Expand Down
56 changes: 37 additions & 19 deletions lib/index.js
Expand Up @@ -5,15 +5,14 @@
'use strict';

const chalk = require('chalk');
const path = require('path');
const semver = require('semver');
const path = require('path');
const semverPrerelease = require('semver/functions/prerelease');
const { exec } = require('child_process');
const { promisify } = require('util');
const validatePackageName = require('validate-npm-package-name');

const { execp, requireModule } = require('./utils');

const { blue, bold, green, red } = chalk;
const execAsync = promisify(exec);

/**
* Validates package name.
Expand All @@ -29,6 +28,7 @@ function isValidNpmPackageName(name) {
}

/**
*
* Gets available versions for provided package name.
*
* @param {string} name - Package name.
Expand All @@ -38,13 +38,13 @@ function isValidNpmPackageName(name) {
async function getLatestVersions(name) {
isValidNpmPackageName(name);
try {
const { stdout } = await execAsync(`npm view ${name} versions --json`);
const { stdout } = await execp(`npm view ${name} versions --json`);
const versions = JSON.parse(stdout);
return Array.isArray(versions) ? versions : [versions];
} catch (err) {
const message =
err instanceof SyntaxError
? `Failed to parse output from NPM view - ${err.toString()}`
? `Failed to parse output from NPM view when getting versions - ${err.toString()}`
: `Error getting latest versions - ${err}`;
throw new Error(message);
}
Expand All @@ -60,13 +60,13 @@ async function getLatestVersions(name) {
async function getLatestTag(name) {
isValidNpmPackageName(name);
try {
const { stdout } = await execAsync(`npm view ${name} dist-tags --json`);
const { stdout } = await execp(`npm view ${name} dist-tags --json`);
const { latest } = JSON.parse(stdout);
return latest;
} catch (err) {
const message =
err instanceof SyntaxError
? `Failed to parse output from NPM view - ${err.toString()}`
? `Failed to parse output from NPM view when getting tags - ${err.toString()}`
: `Error getting latest tag - ${err}`;
throw new Error(message);
}
Expand All @@ -84,7 +84,6 @@ async function getLatestVersion(name, wanted) {
const versions = await getLatestVersions(name);
const latest = await getLatestTag(name);
let applicableVersions = versions.filter((i) => semver.satisfies(i, wanted));

const prereleases = [];
if (semverPrerelease(wanted.slice(1))) {
for (const version of versions) {
Expand Down Expand Up @@ -122,7 +121,8 @@ async function getLatestVersion(name, wanted) {
*/
function getInstalledVersion(currentDir, name, logger) {
try {
return require(path.join(currentDir, 'node_modules', name, 'package.json')).version;
const { version } = requireModule(path.join(currentDir, 'node_modules', name, 'package.json'));
return version;
} catch (err) {
logger.info(`Error getting a list of installed modules from package.json - ${err}`);
return null;
Expand All @@ -133,16 +133,15 @@ function getInstalledVersion(currentDir, name, logger) {
* Builds list of packages to update.
*
* @param {Object} params - Object with parameters.
* @param {*} params.deps - List of dependencies.
* @param {Object.<string, string>} params.deps - List of dependencies.
* @param {string} params.dir - Directory location.
* @param {Logger} params.logger - Logging tool.
* @param {string} params.type - Type of dependency.
* @returns {Array<Promise<PackageStatus>>} - NPM package state.
*/
function pushPkgs({ dir, logger, deps = {}, type }) {
function pushPkgs({ deps, dir, logger, type }) {
return Object.keys(deps).map(async (name) => {
let wanted = deps[name];
if (!wanted.startsWith('^')) wanted = `^${wanted}`;
const wanted = deps[name];
const installed = getInstalledVersion(dir, name, logger);
const latest = await getLatestVersion(name, wanted);
const wantedFixed = wanted.slice(1);
Expand Down Expand Up @@ -171,6 +170,25 @@ function getPkgIds(filteredPkgs) {
return filteredPkgs.map(({ latest, name }) => `${name}@${latest}`).join(' ');
}

/**
* Filters out dependencies with locked versions.
*
* @param {Object.<string, string>} deps - List of dependencies.
* @returns {Object.<string, string> | {}} List of dependencies excluding locked semver versions.
*/
function removeLockedDependencies(deps) {
let depsToUpgrade = {};

for (const name of Object.keys(deps)) {
const version = deps[name];
const firstChar = version.charAt(0);
if (firstChar === '^' || firstChar === '~') {
depsToUpgrade = { ...depsToUpgrade, [name]: version };
hy2999la marked this conversation as resolved.
Show resolved Hide resolved
}
}
return depsToUpgrade;
}

/**
* Verifies the dependencies listed in the package.json of the given directory.
*
Expand All @@ -181,12 +199,12 @@ function getPkgIds(filteredPkgs) {
* @param {Logger} [options.logger] - A logger instance, with a similar API as the console object.
*/
async function verifyDeps({ autoUpgrade = false, dir = '', logger = console } = {}) {
const { dependencies, devDependencies } = require(path.join(dir, 'package.json'));
const { dependencies = {}, devDependencies = {} } = requireModule(path.join(dir, 'package.json'));
logger.info(blue('Verifying dependencies…\n'));

const pkgs = await Promise.all([
...pushPkgs({ deps: dependencies, dir, logger, type: 'prod' }),
...pushPkgs({ deps: devDependencies, dir, logger, type: 'dev' })
...pushPkgs({ deps: removeLockedDependencies(dependencies), dir, logger, type: 'prod' }),
...pushPkgs({ deps: removeLockedDependencies(devDependencies), dir, logger, type: 'dev' })
]);
const toInstall = pkgs.filter(({ shouldBeInstalled }) => shouldBeInstalled);
if (toInstall.length > 0) {
Expand All @@ -203,8 +221,8 @@ async function verifyDeps({ autoUpgrade = false, dir = '', logger = console } =
if (autoUpgrade) {
logger.info('UPGRADING…');
logger.info(upgradePackages);
const prodResult = await execAsync(`npm i ${getPkgIds(prodPkgs)}`);
const devResult = await execAsync(`npm i -D ${getPkgIds(devPkgs)}`);
const prodResult = await execp(`npm i ${getPkgIds(prodPkgs)}`);
const devResult = await execp(`npm i -D ${getPkgIds(devPkgs)}`);
logger.info(`${green(`${bold('Upgraded dependencies:\n')}${prodResult.stdout}`)}`);
logger.info(`${green(`${bold('Upgraded development dependencies:\n')}${devResult.stdout}`)}`);
} else {
Expand Down