Skip to content

Commit

Permalink
feat(core): add format command
Browse files Browse the repository at this point in the history
  • Loading branch information
JamieMason committed Feb 10, 2018
1 parent 2a3ce5d commit bae1133
Show file tree
Hide file tree
Showing 12 changed files with 281 additions and 8 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"author": "Jamie Mason <jamie@foldleft.io> (https://github.com/JamieMason)",
"bin": {
"syncpack": "dist/bin.js",
"syncpack-format": "dist/bin-format.js",
"syncpack-fix-mismatches": "dist/bin-fix-mismatches.js",
"syncpack-list-mismatches": "dist/bin-list-mismatches.js",
"syncpack-list": "dist/bin-list.js"
Expand Down
18 changes: 18 additions & 0 deletions src/bin-format.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/usr/bin/env node

import chalk from 'chalk';
import * as program from 'commander';
import * as _ from 'lodash';
import { relative } from 'path';
import { DEFAULT_PATTERN, OPTION_PACKAGES } from './constants';
import { format } from './manifests';

program.option(OPTION_PACKAGES.spec, OPTION_PACKAGES.description).parse(process.argv);

const { packages = DEFAULT_PATTERN } = program;

format(packages).then((descriptors) => {
_.each(descriptors, (descriptor) => {
console.log(chalk.blue(`./${relative('.', descriptor.path)}`));
});
});
3 changes: 2 additions & 1 deletion src/bin.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
#!/usr/bin/env node

import * as program from 'commander';
import { FIX_MISMATCHES, LIST, LIST_MISMATCHES } from './constants';
import { FIX_MISMATCHES, FORMAT, LIST, LIST_MISMATCHES } from './constants';

program
.version('TODO')
.command(FIX_MISMATCHES.name, FIX_MISMATCHES.description)
.command(FORMAT.name, FORMAT.description)
.command(LIST.name, LIST.description, { isDefault: true })
.command(LIST_MISMATCHES.name, LIST_MISMATCHES.description);

Expand Down
7 changes: 7 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ export const FIX_MISMATCHES = {
description: 'set dependencies used with different versions to the same version',
name: 'fix-mismatches'
};
export const FORMAT = {
description: 'sort and shorten properties according to a convention',
name: 'format'
};
export const LIST = {
description: 'list every dependency used in your packages',
name: 'list'
Expand All @@ -22,3 +26,6 @@ export const OPTION_PACKAGES = {
};
export const SAME = 0;
export const SEMVER_ORDER = ['<', '<=', '', '~', '^', '>=', '>', '*'];
export const SORT_AZ = ['dependencies', 'devDependencies', 'files', 'keywords', 'peerDependencies', 'scripts'];
export const SORT_FIRST = ['name', 'description', 'version', 'author'];
export const VERSION = execSync(`npm view ${__dirname} version`, { encoding: 'utf8' });
18 changes: 17 additions & 1 deletion src/manifests/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@ import { readFileSync } from 'fs';
import * as mock from 'mock-fs';
import { createFile, createManifest, createMockDescriptor, createMockFs } from '../../test/helpers';
import { IManifest, IManifestDescriptor } from '../typings';
import { getMismatchedVersions, getVersions, setVersion, setVersionRange, setVersionsToNewestMismatch } from './index';
import {
format,
getMismatchedVersions,
getVersions,
setVersion,
setVersionRange,
setVersionsToNewestMismatch
} from './index';

const pattern = '/Users/you/Dev/monorepo/packages/*/package.json';

Expand All @@ -23,6 +30,15 @@ beforeEach(() => {
});
});

describe('format', () => {
it('sorts and shortens properties according to a convention', async () => {
const result = await format(pattern);
result.forEach((descriptor: IManifestDescriptor) => {
expect(Object.keys(descriptor.data)).toEqual(['name', 'dependencies', 'devDependencies', 'peerDependencies']);
});
});
});

describe('getMismatchedVersions', () => {
it('returns an index of dependencies used with different versions', async () => {
const result = await getMismatchedVersions(pattern);
Expand Down
15 changes: 15 additions & 0 deletions src/manifests/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { IDictionary, IManifest, IManifestDescriptor } from '../typings';
import { getManifests } from './get-manifests';
import { manifestData } from './manifest-data';

export type Format = (pattern: string) => Promise<IManifestDescriptor[]>;
export type GetMismatchedVersions = (pattern: string) => Promise<IDictionary<string[]>>;
export type GetVersions = (pattern: string) => Promise<IDictionary<string[]>>;
export type SetVersion = (name: string, version: string, pattern: string) => Promise<IManifestDescriptor[]>;
Expand All @@ -11,6 +12,20 @@ export type SetVersionsToNewestMismatch = (pattern: string) => Promise<IManifest

const unwrap = (descriptors: IManifestDescriptor[]) => descriptors.map((descriptor) => descriptor.data);

export const format: Format = (pattern) =>
getManifests(pattern)
.then((descriptors) => {
const data = unwrap(descriptors);
const nextData = manifestData.format(data);
return descriptors.map((descriptor, i) => ({
data: nextData[i],
path: descriptor.path
}));
})
.then((descriptors) =>
Promise.all(descriptors.map((descriptor) => writeJson(descriptor.path, descriptor.data))).then(() => descriptors)
);

export const getMismatchedVersions: GetMismatchedVersions = (pattern) =>
getManifests(pattern)
.then(unwrap)
Expand Down
91 changes: 91 additions & 0 deletions src/manifests/manifest-data/format.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { getUntidyManifest } from '../../../test/fixtures';
import { shuffleObject } from '../../../test/helpers';
import { SORT_FIRST } from '../../constants';
import { IManifest } from '../../typings';
import { manifestData } from './index';

describe('format', () => {
let results: IManifest[];
beforeAll(() => {
results = manifestData.format([shuffleObject(getUntidyManifest()) as IManifest]);
});

it('sorts specified keys to the top of package.json', () => {
results.forEach((result) => {
expect(Object.keys(result).slice(0, SORT_FIRST.length)).toEqual(SORT_FIRST);
});
});

it('sorts remaining keys alphabetically', () => {
results.forEach((result) => {
expect(Object.keys(result).slice(SORT_FIRST.length - 1)).toEqual([
'author',
'bin',
'bugs',
'dependencies',
'devDependencies',
'files',
'homepage',
'keywords',
'license',
'main',
'peerDependencies',
'repository',
'scripts'
]);
});
});

it('sorts "dependencies" alphabetically', () => {
results.forEach((result) => {
expect(result.dependencies).toEqual({
arnold: '5.0.0',
dog: '2.13.0',
guybrush: '7.1.1',
mango: '2.3.0'
});
});
});

it('sorts "devDependencies" alphabetically', () => {
results.forEach((result) => {
expect(result.devDependencies).toEqual({ stroopwafel: '4.4.2', waldorf: '22.1.4' });
});
});

it('sorts "files" alphabetically', () => {
results.forEach((result) => {
expect(result.files).toEqual(['assets', 'dist']);
});
});

it('sorts "keywords" alphabetically', () => {
results.forEach((result) => {
expect(result.keywords).toEqual(['thing', 'those', 'whatsits']);
});
});

it('sorts "peerDependencies" alphabetically', () => {
results.forEach((result) => {
expect(result.peerDependencies).toEqual({ giftwrap: '0.1.2', jambalaya: '6.1.4', zoolander: '1.4.25' });
});
});

it('sorts "scripts" alphabetically', () => {
results.forEach((result) => {
expect(result.scripts).toEqual({ build: 'tsc', format: 'prettier', lint: 'tslint', test: 'jest' });
});
});

it('uses shorthand "bugs"', () => {
results.forEach((result) => {
expect(result.bugs).toEqual('https://github.com/JaneDoe/do-it/issues');
});
});

it('uses shorthand "repository"', () => {
results.forEach((result) => {
expect(result.repository).toEqual('JaneDoe/do-it');
});
});
});
56 changes: 56 additions & 0 deletions src/manifests/manifest-data/format.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import * as _ from 'lodash';
import { SORT_AZ, SORT_FIRST } from '../../constants';
import { IDictionary, IManifest } from '../../typings';

export type Format = (manifests: IManifest[]) => IManifest[];
export type ManifestMapper = (manifest: IManifest) => IManifest;

const shortenBugs: ManifestMapper = (manifest: IManifest) => {
if (manifest.bugs && typeof manifest.bugs === 'object' && manifest.bugs.url) {
return {
...manifest,
bugs: manifest.bugs.url
};
}
return manifest;
};

const shortenRepository: ManifestMapper = (manifest) => {
if (
manifest.repository &&
typeof manifest.repository === 'object' &&
manifest.repository.url &&
manifest.repository.url.indexOf('github.com') !== -1
) {
return {
...manifest,
repository: manifest.repository.url.split('github.com/')[1]
};
}
return manifest;
};

const sortObject = (obj: IManifest) =>
_(obj)
.entries()
.sortBy('0')
.reduce((next, [key, value]) => ({ ...next, [key]: value }), {});

const sortValue = (value: any) =>
_.isArray(value) ? value.slice(0).sort() : _.isObject(value) ? sortObject(value) : value;

const sortManifest: ManifestMapper = (manifest) => {
const [first, rest] = _(manifest)
.entries()
.sortBy('0')
.partition(([key, value]) => SORT_FIRST.indexOf(key) !== -1)
.value();
const firstSorted = [...first].sort(([keyA], [keyB]) => SORT_FIRST.indexOf(keyA) - SORT_FIRST.indexOf(keyB));
const restSorted = _(rest)
.map(([key, value]) => [key, SORT_AZ.indexOf(key) !== -1 ? sortValue(value) : value])
.value();
return _([...firstSorted, ...restSorted]).reduce((obj, [key, value]) => ({ ...obj, [key]: value }), {} as IManifest);
};

export const format: Format = (manifests) =>
_.map(manifests, (manifest) => sortManifest(shortenBugs(shortenRepository(manifest))));
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { createManifest } from '../../test/helpers';
import { IManifest } from '../typings';
import { manifestData } from './manifest-data';
import { createManifest } from '../../../test/helpers';
import { IManifest } from '../../typings';
import { manifestData } from './index';

const { getMismatchedVersions, getVersions, setVersion, setVersionRange, setVersionsToNewestMismatch } = manifestData;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import * as _ from 'lodash';
import * as semver from 'semver';
import { DEPENDENCY_TYPES } from '../constants';
import { IDictionary, IManifest } from '../typings';
import { getNewest, isValid } from '../version';
import { DEPENDENCY_TYPES } from '../../constants';
import { IDictionary, IManifest } from '../../typings';
import { getNewest, isValid } from '../../version';
import { format } from './format';

export type GetMismatchedVersions = (manifests: IManifest[]) => IDictionary<string[]>;
export type GetVersions = (manifests: IManifest[]) => IDictionary<string[]>;
Expand Down Expand Up @@ -94,6 +95,7 @@ const setVersionsToNewestMismatch: SetVersionsToNewestMismatch = (manifests) =>
};

export const manifestData = {
format,
getMismatchedVersions,
getVersions,
isManifest,
Expand Down
55 changes: 55 additions & 0 deletions test/fixtures.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { IManifest } from '../src/typings';

export const getUntidyManifest = (): IManifest =>
JSON.parse(`{
"files": [
"assets",
"dist"
],
"bugs": {
"url": "https://github.com/JaneDoe/do-it/issues"
},
"author": "Jane Doe <jane@doe.io>",
"devDependencies": {
"waldorf": "22.1.4",
"stroopwafel": "4.4.2"
},
"scripts": {
"test": "jest",
"build": "tsc",
"lint": "tslint",
"format": "prettier"
},
"version": "1.0.2",
"main": "do-it",
"license": "MIT",
"description": "Does the thing",
"homepage": "https://github.com/JaneDoe/do-it#readme",
"dependencies": {
"guybrush": "7.1.1",
"arnold": "5.0.0",
"dog": "2.13.0",
"mango": "2.3.0"
},
"name": "do-it",
"repository": {
"url": "git://github.com/JaneDoe/do-it",
"type": "git"
},
"keywords": [
"those",
"whatsits",
"thing"
],
"bin": {
"zoo": "dist/zoo.js",
"moose": "dist/moose.js",
"apple": "dist/apple.js"
},
"peerDependencies": {
"jambalaya": "6.1.4",
"giftwrap": "0.1.2",
"zoolander": "1.4.25"
}
}
`);
11 changes: 11 additions & 0 deletions test/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import * as _ from 'lodash';
import { formatJson } from '../src/lib/write-json';
import { IDictionary, IManifest, IManifestDescriptor } from '../src/typings';

Expand Down Expand Up @@ -43,3 +44,13 @@ export const createManifest = (
name,
peerDependencies
});

const shuffle = (value: any): typeof value =>
_.isArray(value) ? _.shuffle(value) : _.isObject(value) ? shuffleObject(value) : value;

export const shuffleObject = (obj: object): object =>
_(obj)
.entries()
.map(([key, value]) => [key, shuffle(value)])
.shuffle()
.reduce((next, [key, value]) => ({ ...next, [key]: value }), {});

0 comments on commit bae1133

Please sign in to comment.