Compare commits

...

9 Commits

Author SHA1 Message Date
f8d78d05fc fix: handle alternate workspaces config 2019-04-08 11:10:13 -07:00
6e2288f157 docs: update readme 2019-04-05 15:55:41 -07:00
9aebeb31a6 feat: support workspaces 2019-04-05 15:49:59 -07:00
b93defbf4d fix: compare stringified objects
fixes issue with packages that have no dependencies or devDependencies
2019-04-05 15:49:16 -07:00
4cd6e2922c feat: provide package info instead of just ident
useful for reading other properties out of the package.json file
2019-04-05 15:46:10 -07:00
9fb86bf32e fix: fail on ident read
remove getRootFileContents util, easily replaced in the one place it's used
2019-04-05 15:00:34 -07:00
71f8bb7729 feat: show skip output in verbose mode 2019-04-04 17:33:53 -07:00
4fec26c796 chore: add test script 2019-04-04 17:13:25 -07:00
bce054516b chore: update repository url 2019-04-04 17:11:55 -07:00
7 changed files with 76 additions and 16 deletions

View File

@@ -6,6 +6,17 @@ Node package compare and change runner.
[![npm](https://img.shields.io/npm/v/pkgcomp.svg)](https://www.npmjs.com/package/pkgcomp)
[![Project Status](https://img.shields.io/badge/status-experimental-orange.svg)](https://nodejs.org/api/documentation.html#documentation_stability_index)
This tool will execute a command if the dependencies or lock files of your module have changed since the last time it was run. Any of the following will be considered a change:
- If the `dependencies` or `devDependencies` in `package.json` change
- If the `dependencies` or `devDependencies` in any of your workspaces change
- Uses `workspaces` property in root `package.json`, works with Yarn workspaces
- If one of the files in your `checkFiles` config's md5 hash changes
If the command exits with a non-zero exit code, `pkgcomp` will exit with the same exit code.
**NOTE:** Out of the box the script will do nothing, you have to provide a command to run, via the `cmd` config. See [Configuration](#configuration) below.
## Usage
Simply install the package and run it from the command line in your project. You can install it globally and run it that way, but installing locally is generally better.
@@ -15,9 +26,7 @@ yarn add -D pkgcomp
npx pkgcomp
```
If the `dependencies` or `devDependencies` in `package.json` change, or if one of the files in your `checkFiles` config's md5 hash changes, your command will run. If the command exits with a non-zero exit code, `pkgcomp` will exit with the same exit code.
Out of the box the script will do nothing, you have to provide a command to run, via the `cmd` config. See [Configuration](#configuration) below.
**NOTE:** Out of the box the script will do nothing, you have to provide a command to run, via the `cmd` config. See [Configuration](#configuration) below.
You can also set the command, and control how the script works, via the available [command line arguments](#cli-arguments)

View File

@@ -9,11 +9,12 @@
"scripts": {
"lint": "eslint 'src/**/*.{js,mjs}'",
"version": "npm-auto-version",
"start": "node ."
"start": "node .",
"test": "npm run lint"
},
"repository": {
"type": "git",
"url": "git+https://github.com/w33ble/pkgcomp.git"
"url": "git+https://git.w33ble.com/w33ble/pkgcomp.git"
},
"keywords": [
"spawn",
@@ -63,7 +64,7 @@
"trailingComma": "es5"
},
"dependencies": {
"fast-deep-equal": "^2.0.1",
"glob-parent": "^5.0.0",
"joycon": "^2.2.4",
"md5-file": "^4.0.0",
"mkdirp": "^0.5.1",

View File

@@ -36,7 +36,9 @@ function buildConfig(args) {
// parse args from CLI
const args = parseArgs();
pkgcomp(buildConfig(args), utils.getIdent(), args).catch(err => {
const { name } = utils.getPackageInfo();
pkgcomp(buildConfig(args), name, args).catch(err => {
// eslint-disable-next-line no-console
console.error(err);
process.exit(err.exitCode);

View File

@@ -18,7 +18,7 @@ exports.read = (fileRoot, ident) => {
try {
const data = JSON.parse(fs.readFileSync(filePath));
return data[ident] || {};
return JSON.stringify(data[ident] || {});
} catch (err) {
if (err.code === 'ENOENT') {
fs.writeFileSync(filePath, JSON.stringify({}));

View File

@@ -1,6 +1,5 @@
const path = require('path');
const md5File = require('md5-file');
const deepEqual = require('fast-deep-equal');
const shellExec = require('shell-exec');
const data = require('./data');
const utils = require('./utils');
@@ -43,14 +42,25 @@ module.exports = async function pkgcomp(config, ident, opts = defaultOptions) {
// read dependencies from package.json
const pkg = utils.getFileContents(path.join(config.rootDir, 'package.json'), { format: 'json' });
// read dependencies from workspace packages
const workspaces = utils.getPackageWorkspaces().map(({ name, package }) => {
const wpkg = utils.getFileContents(path.join(config.rootDir, package), { format: 'json' });
return {
name,
dependencies: wpkg.dependencies,
devDependencies: wpkg.devDependencies,
};
});
const payload = {
hashes,
dependencies: pkg.dependencies,
devDependencies: pkg.devDependencies,
workspaces,
};
// check if the command should be run
const runCmd = Boolean(opts.force || (doExec && !deepEqual(packageHistory, payload)));
const runCmd = Boolean(opts.force || (doExec && packageHistory !== JSON.stringify(payload)));
if (runCmd) {
const res = await execCommand(config.cmd);
@@ -60,6 +70,9 @@ module.exports = async function pkgcomp(config, ident, opts = defaultOptions) {
err.cmd = res.cmd;
throw err;
}
} else if (opts.verbose) {
// eslint-disable-next-line no-console
console.log('No changes, command skipped');
}
data.write(config.dataDir, ident, payload);

View File

@@ -2,6 +2,7 @@ const path = require('path');
const fs = require('fs');
const os = require('os');
const JoyCon = require('joycon');
const globParent = require('glob-parent');
exports.getRootPath = () => process.cwd();
@@ -27,13 +28,40 @@ exports.getFileContents = (filePath, opts = {}) => {
return content;
};
exports.getRootFileContents = (filename, opts = {}) => {
return exports.getFileContents(path.join(exports.getRootPath(), filename), opts);
exports.getPackageInfo = () => {
const { name, workspaces } = exports.getFileContents(
path.join(exports.getRootPath(), 'package.json'),
{
format: 'json',
}
);
if (!name) throw new Error('Unable to read project name from package.json');
// handle nohoist config: https://yarnpkg.com/blog/2018/02/15/nohoist/
const realWorkspaces = workspaces.packages ? workspaces.packages : workspaces;
return { name, workspaces: realWorkspaces };
};
exports.getIdent = () => {
const { name } = exports.getRootFileContents('package.json', { format: 'json' });
return name;
exports.getPackageWorkspaces = () => {
const { workspaces } = exports.getPackageInfo();
if (!workspaces) return [];
return workspaces.reduce((acc, glob) => {
const parent = globParent(glob);
const packages = fs
.readdirSync(parent)
.map(package => {
const packageJson = path.join(parent, package, 'package.json');
if (!exports.canAccessFile(packageJson)) return false;
return {
name: package,
package: packageJson,
};
})
.filter(Boolean);
return acc.concat(packages);
}, []);
};
exports.getConfig = (overrides = {}) => {

View File

@@ -1027,6 +1027,13 @@ getopts@^2.0.6:
resolved "https://registry.yarnpkg.com/getopts/-/getopts-2.2.4.tgz#3137fe8a5fddf304904059a851bdc1c22f0f54fb"
integrity sha512-Rz7DGyomZjrenu9Jx4qmzdlvJgvrEFHXHvjK0FcZtcTC1U5FmES7OdZHUwMuSnEE6QvBvwse1JODKj7TgbSEjQ==
glob-parent@^5.0.0:
version "5.0.0"
resolved "https://npm.w33ble.com:443/glob-parent/-/glob-parent-5.0.0.tgz#1dc99f0f39b006d3e92c2c284068382f0c20e954"
integrity sha512-Z2RwiujPRGluePM6j699ktJYxmPpJKCfpGA13jz2hmFZC7gKetzrWvg5KN3+OsIFmydGyZ1AVwERCq1w/ZZwRg==
dependencies:
is-glob "^4.0.1"
glob@^7.0.3, glob@^7.1.2, glob@^7.1.3:
version "7.1.3"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1"
@@ -1324,7 +1331,7 @@ is-fullwidth-code-point@^2.0.0:
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=
is-glob@^4.0.0:
is-glob@^4.0.0, is-glob@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc"
integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==