Skip to content

Commit

Permalink
refactor(ADTs): propagate all errors
Browse files Browse the repository at this point in the history
  • Loading branch information
JamieMason committed Feb 21, 2023
1 parent f0869db commit b5ceae0
Show file tree
Hide file tree
Showing 17 changed files with 278 additions and 166 deletions.
13 changes: 10 additions & 3 deletions src/bin-fix-mismatches/fix-mismatches.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { pipe, R } from '@mobily/ts-belt';
import { isObject } from 'expect-more/dist/is-object';
import { isUndefined } from 'expect-more/dist/is-undefined';
import { $R } from '../get-context/$R';
import type { Syncpack } from '../types';

export function fixMismatches(ctx: Syncpack.Ctx): Syncpack.Ctx {
Expand All @@ -12,9 +14,14 @@ export function fixMismatches(ctx: Syncpack.Ctx): Syncpack.Ctx {
// Set the correct version on each instance.
invalidGroups.forEach((instanceGroup) => {
if (!instanceGroup.hasUnsupportedVersion()) {
const nextVersion = instanceGroup.getExpectedVersion();
instanceGroup.instances.forEach((instance) =>
instance.setVersion(nextVersion),
pipe(
instanceGroup.getExpectedVersion(),
R.tap((nextVersion) => {
instanceGroup.instances.forEach((instance) =>
instance.setVersion(nextVersion),
);
}),
$R.tapErrVerbose,
);
}
});
Expand Down
3 changes: 2 additions & 1 deletion src/bin-format/format.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import { isObject } from 'expect-more/dist/is-object';
import type { Syncpack } from '../types';

export function format(ctx: Syncpack.Ctx): Syncpack.Ctx {
const { sortAz, sortFirst, packageJsonFiles } = ctx;
const { packageJsonFiles } = ctx;
const { sortAz, sortFirst } = ctx.config;

packageJsonFiles.forEach((packageJsonFile) => {
const { contents } = packageJsonFile;
Expand Down
19 changes: 11 additions & 8 deletions src/bin-list-mismatches/list-mismatches.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { O, R } from '@mobily/ts-belt';
import chalk from 'chalk';
import type { InstanceGroup } from '../get-context/get-groups/version-group/instance-group';
import type { Instance } from '../get-context/get-package-json-files/package-json-file/instance';
Expand Down Expand Up @@ -58,7 +59,7 @@ export function listMismatches(ctx: Syncpack.Ctx): Syncpack.Ctx {

function logUnpinned(instanceGroup: InstanceGroup) {
const name = instanceGroup.name;
const pinVersion = instanceGroup.versionGroup.getPinnedVersion();
const pinVersion = O.getExn(instanceGroup.versionGroup.getPinnedVersion());
log.invalid(
name,
chalk`is pinned in this version group at {reset.green ${pinVersion}}`,
Expand All @@ -74,12 +75,14 @@ export function listMismatches(ctx: Syncpack.Ctx): Syncpack.Ctx {
function logSnappedTo(instanceGroup: InstanceGroup) {
const name = instanceGroup.name;
const versionGroup = instanceGroup.versionGroup;
const snappedVersion = instanceGroup.getSnappedVersion();
const packages = versionGroup.getSnappedToPackages().join(' || ');
const version = instanceGroup.getExpectedVersion();
const snappedVersion = R.getExn(instanceGroup.getSnappedVersion());
const snappedToPackages = O.getExn(
versionGroup.getSnappedToPackages(),
).join(' || ');
const version = R.getExn(instanceGroup.getExpectedVersion());
log.invalid(
name,
chalk`should snap to {reset.green ${version}}, used by ${packages}`,
chalk`should snap to {reset.green ${version}}, used by ${snappedToPackages}`,
);
// Log each of the dependencies mismatches
instanceGroup.instances.forEach((instance) => {
Expand All @@ -91,9 +94,9 @@ export function listMismatches(ctx: Syncpack.Ctx): Syncpack.Ctx {

function logWorkspaceMismatch(instanceGroup: InstanceGroup) {
const name = instanceGroup.name;
const workspaceInstance = instanceGroup.getWorkspaceInstance();
const workspaceInstance = O.getExn(instanceGroup.getWorkspaceInstance());
const shortPath = workspaceInstance?.packageJsonFile.shortPath;
const expected = instanceGroup.getExpectedVersion();
const expected = R.getExn(instanceGroup.getExpectedVersion());
log.invalid(
name,
chalk`{reset.green ${expected}} {dim is developed in this repo at ${shortPath}}`,
Expand All @@ -108,7 +111,7 @@ export function listMismatches(ctx: Syncpack.Ctx): Syncpack.Ctx {

function logHighestVersionMismatch(instanceGroup: InstanceGroup) {
const name = instanceGroup.name;
const expected = instanceGroup.getExpectedVersion();
const expected = R.getExn(instanceGroup.getExpectedVersion());
log.invalid(
name,
chalk`{reset.green ${expected}} {dim is the highest valid semver version in use}`,
Expand Down
28 changes: 18 additions & 10 deletions src/bin-list/list.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { O, pipe, R } from '@mobily/ts-belt';
import chalk from 'chalk';
import { ICON } from '../constants';
import { $R } from '../get-context/$R';
import type { InstanceGroup } from '../get-context/get-groups/version-group/instance-group';
import * as log from '../lib/log';
import type { Syncpack } from '../types';
Expand Down Expand Up @@ -44,15 +46,21 @@ export function list(ctx: Syncpack.Ctx): Syncpack.Ctx {
}

function logVersionMismatch(instanceGroup: InstanceGroup): void {
console.log(
chalk`{red ${ICON.cross} ${instanceGroup.name}} ${instanceGroup
.getUniqueVersions()
.map((version) =>
version === instanceGroup.getExpectedVersion()
? chalk.green(version)
: chalk.red(version),
)
.join(chalk.dim(', '))}`,
pipe(
instanceGroup.getExpectedVersion(),
R.tap((expectedVersion) => {
const uniqueVersions = instanceGroup.getUniqueVersions();
console.log(
chalk`{red ${ICON.cross} ${instanceGroup.name}} ${uniqueVersions
.map((version) =>
version === expectedVersion
? chalk.green(version)
: chalk.red(version),
)
.join(chalk.dim(', '))}`,
);
}),
$R.tapErrVerbose,
);
}

Expand All @@ -69,7 +77,7 @@ export function list(ctx: Syncpack.Ctx): Syncpack.Ctx {
}

function logUnpinned(instanceGroup: InstanceGroup): void {
const pinVersion = instanceGroup.versionGroup.getPinnedVersion();
const pinVersion = O.getExn(instanceGroup.versionGroup.getPinnedVersion());
console.log(
chalk`{red ${ICON.cross} ${instanceGroup.name}} {dim.red is pinned to ${pinVersion} in this version group}`,
);
Expand Down
9 changes: 0 additions & 9 deletions src/get-context/get-all-instances.ts

This file was deleted.

40 changes: 23 additions & 17 deletions src/get-context/get-config/get-config.spec.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
import { R } from '@mobily/ts-belt';
import 'expect-more-jest';
import { getConfig } from '.';
import { mockDisk } from '../../../test/mock-disk';
import type { BaseError } from '../../lib/error';
import type { Syncpack } from '../../types';

describe('enabledTypes', () => {
const all = 'dev,overrides,peer,pnpmOverrides,prod,resolutions,workspace';
const getNames = (config) => config.enabledTypes.map(({ name }) => name);

function getNames(config: R.Result<Syncpack.Config.Private, BaseError>) {
return R.getExn(config).enabledTypes.map(({ name }) => name);
}

it('enables all when nothing is set', () => {
const disk = mockDisk();
Expand Down Expand Up @@ -53,110 +59,110 @@ describe('filter', () => {
it('uses default when not set', () => {
const disk = mockDisk();
const config = getConfig(disk, {});
expect(config.filter).toEqual('.');
expect(R.getExn(config).filter).toEqual('.');
});

it('uses CLI value when set', () => {
const disk = mockDisk();
const config = getConfig(disk, { filter: 'foo' });
expect(config.filter).toEqual('foo');
expect(R.getExn(config).filter).toEqual('foo');
});

it('uses config value when set', () => {
const disk = mockDisk();
disk.readConfigFileSync.mockReturnValue({ filter: 'bar' });
const config = getConfig(disk, {});
expect(config.filter).toEqual('bar');
expect(R.getExn(config).filter).toEqual('bar');
});

it('uses CLI value when config and CLI are set', () => {
const disk = mockDisk();
disk.readConfigFileSync.mockReturnValue({ filter: 'bar' });
const config = getConfig(disk, { filter: 'foo' });
expect(config.filter).toEqual('foo');
expect(R.getExn(config).filter).toEqual('foo');
});
});

describe('indent', () => {
it('uses default when not set', () => {
const disk = mockDisk();
const config = getConfig(disk, {});
expect(config.indent).toEqual(' ');
expect(R.getExn(config).indent).toEqual(' ');
});

it('uses CLI value when set', () => {
const disk = mockDisk();
const config = getConfig(disk, { indent: '\t' });
expect(config.indent).toEqual('\t');
expect(R.getExn(config).indent).toEqual('\t');
});

it('uses config value when set', () => {
const disk = mockDisk();
disk.readConfigFileSync.mockReturnValue({ indent: '\t' });
const config = getConfig(disk, {});
expect(config.indent).toEqual('\t');
expect(R.getExn(config).indent).toEqual('\t');
});

it('uses CLI value when config and CLI are set', () => {
const disk = mockDisk();
disk.readConfigFileSync.mockReturnValue({ indent: '\t' });
const config = getConfig(disk, { indent: ' ' });
expect(config.indent).toEqual(' ');
expect(R.getExn(config).indent).toEqual(' ');
});
});

describe('semverRange', () => {
it('uses default when not set', () => {
const disk = mockDisk();
const config = getConfig(disk, {});
expect(config.semverRange).toEqual('');
expect(R.getExn(config).semverRange).toEqual('');
});

it('uses CLI value when set', () => {
const disk = mockDisk();
const config = getConfig(disk, { semverRange: '^' });
expect(config.semverRange).toEqual('^');
expect(R.getExn(config).semverRange).toEqual('^');
});

it('uses config value when set', () => {
const disk = mockDisk();
disk.readConfigFileSync.mockReturnValue({ semverRange: '~' });
const config = getConfig(disk, {});
expect(config.semverRange).toEqual('~');
expect(R.getExn(config).semverRange).toEqual('~');
});

it('uses CLI value when config and CLI are set', () => {
const disk = mockDisk();
disk.readConfigFileSync.mockReturnValue({ semverRange: '^' });
const config = getConfig(disk, { semverRange: '*' });
expect(config.semverRange).toEqual('*');
expect(R.getExn(config).semverRange).toEqual('*');
});
});

describe('source', () => {
it('uses default when not set', () => {
const disk = mockDisk();
const config = getConfig(disk, {});
expect(config.source).toEqual([]);
expect(R.getExn(config).source).toEqual([]);
});

it('uses CLI value when set', () => {
const disk = mockDisk();
const config = getConfig(disk, { source: ['apps/*'] });
expect(config.source).toEqual(['apps/*']);
expect(R.getExn(config).source).toEqual(['apps/*']);
});

it('uses config value when set', () => {
const disk = mockDisk();
disk.readConfigFileSync.mockReturnValue({ source: ['projects/*'] });
const config = getConfig(disk, {});
expect(config.source).toEqual(['projects/*']);
expect(R.getExn(config).source).toEqual(['projects/*']);
});

it('uses CLI value when config and CLI are set', () => {
const disk = mockDisk();
disk.readConfigFileSync.mockReturnValue({ source: ['projects/*'] });
const config = getConfig(disk, { source: ['apps/*'] });
expect(config.source).toEqual(['apps/*']);
expect(R.getExn(config).source).toEqual(['apps/*']);
});
});
19 changes: 16 additions & 3 deletions src/get-context/get-config/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { pipe, R } from '@mobily/ts-belt';
import type { Disk } from '../../lib/disk';
import { BaseError } from '../../lib/error';
import { verbose } from '../../lib/log';
import type { Syncpack } from '../../types';
import { getCoreTypes } from './get-core-types';
Expand All @@ -10,10 +12,21 @@ import * as ConfigSchema from './schema';
* Take all configuration from the command line and config file, combine it, and
* set defaults for anything which hasn't been defined.
*/
export const getConfig = (
export function getConfig(
disk: Disk,
fromCli: Partial<Syncpack.Config.Cli>,
): Syncpack.Config.Private => {
): R.Result<Syncpack.Config.Private, BaseError> {
const ERR_READING_CONFIG = 'Error reading config';
return pipe(
R.fromExecution(() => unSafeGetConfig(disk, fromCli)),
R.mapError(BaseError.map(ERR_READING_CONFIG)),
);
}

function unSafeGetConfig(
disk: Disk,
fromCli: Partial<Syncpack.Config.Cli>,
): Syncpack.Config.Private {
verbose('cli arguments:', fromCli);

const fromRcFile = disk.readConfigFileSync(fromCli.configPath);
Expand Down Expand Up @@ -71,4 +84,4 @@ export const getConfig = (
if (typeof (fromRcFile as any)[name] !== 'undefined')
return (fromRcFile as Syncpack.Config.Public)[name];
}
};
}
14 changes: 8 additions & 6 deletions src/get-context/get-context.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,31 @@ describe('getContext', () => {
it('uses defaults when no CLI options or config are set', () => {
const disk = mockDisk();
expect(getContext({}, disk)).toHaveProperty(
'source',
'config.source',
DEFAULT_CONFIG.source,
);
});

it('uses value from config when no CLI options are set', () => {
const disk = mockDisk();
disk.readConfigFileSync.mockReturnValue({ source: ['foo'] });
expect(getContext({}, disk)).toHaveProperty('source', ['foo']);
expect(getContext({}, disk)).toHaveProperty('config.source', ['foo']);
});

it('uses value from CLI when config and CLI options are set', () => {
const disk = mockDisk();
disk.readConfigFileSync.mockReturnValue({ source: ['foo'] });
expect(getContext({ source: ['bar'] }, disk)).toHaveProperty('source', [
'bar',
]);
expect(getContext({ source: ['bar'] }, disk)).toHaveProperty(
'config.source',
['bar'],
);
});

it('combines defaults, values from CLI options, and config', () => {
const disk = mockDisk();
disk.readConfigFileSync.mockReturnValue({ source: ['foo'] });
expect(getContext({ indent: ' ' }, disk)).toEqual(
expect(getContext({ indent: ' ' }, disk)).toHaveProperty(
'config',
expect.objectContaining({
semverRange: '',
source: ['foo'],
Expand Down
2 changes: 1 addition & 1 deletion src/get-context/get-groups/base-group.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export class BaseGroup<
}

/** Add this instance to this group */
add(instance: Instance) {
add(instance: Instance): void {
if (!this.instancesByName[instance.name]) {
this.instancesByName[instance.name] = [];
}
Expand Down

0 comments on commit b5ceae0

Please sign in to comment.