Skip to content

Commit b5ceae0

Browse files
committed
refactor(ADTs): propagate all errors
1 parent f0869db commit b5ceae0

17 files changed

Lines changed: 278 additions & 166 deletions

File tree

src/bin-fix-mismatches/fix-mismatches.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
import { pipe, R } from '@mobily/ts-belt';
12
import { isObject } from 'expect-more/dist/is-object';
23
import { isUndefined } from 'expect-more/dist/is-undefined';
4+
import { $R } from '../get-context/$R';
35
import type { Syncpack } from '../types';
46

57
export function fixMismatches(ctx: Syncpack.Ctx): Syncpack.Ctx {
@@ -12,9 +14,14 @@ export function fixMismatches(ctx: Syncpack.Ctx): Syncpack.Ctx {
1214
// Set the correct version on each instance.
1315
invalidGroups.forEach((instanceGroup) => {
1416
if (!instanceGroup.hasUnsupportedVersion()) {
15-
const nextVersion = instanceGroup.getExpectedVersion();
16-
instanceGroup.instances.forEach((instance) =>
17-
instance.setVersion(nextVersion),
17+
pipe(
18+
instanceGroup.getExpectedVersion(),
19+
R.tap((nextVersion) => {
20+
instanceGroup.instances.forEach((instance) =>
21+
instance.setVersion(nextVersion),
22+
);
23+
}),
24+
$R.tapErrVerbose,
1825
);
1926
}
2027
});

src/bin-format/format.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import { isObject } from 'expect-more/dist/is-object';
44
import type { Syncpack } from '../types';
55

66
export function format(ctx: Syncpack.Ctx): Syncpack.Ctx {
7-
const { sortAz, sortFirst, packageJsonFiles } = ctx;
7+
const { packageJsonFiles } = ctx;
8+
const { sortAz, sortFirst } = ctx.config;
89

910
packageJsonFiles.forEach((packageJsonFile) => {
1011
const { contents } = packageJsonFile;

src/bin-list-mismatches/list-mismatches.ts

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { O, R } from '@mobily/ts-belt';
12
import chalk from 'chalk';
23
import type { InstanceGroup } from '../get-context/get-groups/version-group/instance-group';
34
import type { Instance } from '../get-context/get-package-json-files/package-json-file/instance';
@@ -58,7 +59,7 @@ export function listMismatches(ctx: Syncpack.Ctx): Syncpack.Ctx {
5859

5960
function logUnpinned(instanceGroup: InstanceGroup) {
6061
const name = instanceGroup.name;
61-
const pinVersion = instanceGroup.versionGroup.getPinnedVersion();
62+
const pinVersion = O.getExn(instanceGroup.versionGroup.getPinnedVersion());
6263
log.invalid(
6364
name,
6465
chalk`is pinned in this version group at {reset.green ${pinVersion}}`,
@@ -74,12 +75,14 @@ export function listMismatches(ctx: Syncpack.Ctx): Syncpack.Ctx {
7475
function logSnappedTo(instanceGroup: InstanceGroup) {
7576
const name = instanceGroup.name;
7677
const versionGroup = instanceGroup.versionGroup;
77-
const snappedVersion = instanceGroup.getSnappedVersion();
78-
const packages = versionGroup.getSnappedToPackages().join(' || ');
79-
const version = instanceGroup.getExpectedVersion();
78+
const snappedVersion = R.getExn(instanceGroup.getSnappedVersion());
79+
const snappedToPackages = O.getExn(
80+
versionGroup.getSnappedToPackages(),
81+
).join(' || ');
82+
const version = R.getExn(instanceGroup.getExpectedVersion());
8083
log.invalid(
8184
name,
82-
chalk`should snap to {reset.green ${version}}, used by ${packages}`,
85+
chalk`should snap to {reset.green ${version}}, used by ${snappedToPackages}`,
8386
);
8487
// Log each of the dependencies mismatches
8588
instanceGroup.instances.forEach((instance) => {
@@ -91,9 +94,9 @@ export function listMismatches(ctx: Syncpack.Ctx): Syncpack.Ctx {
9194

9295
function logWorkspaceMismatch(instanceGroup: InstanceGroup) {
9396
const name = instanceGroup.name;
94-
const workspaceInstance = instanceGroup.getWorkspaceInstance();
97+
const workspaceInstance = O.getExn(instanceGroup.getWorkspaceInstance());
9598
const shortPath = workspaceInstance?.packageJsonFile.shortPath;
96-
const expected = instanceGroup.getExpectedVersion();
99+
const expected = R.getExn(instanceGroup.getExpectedVersion());
97100
log.invalid(
98101
name,
99102
chalk`{reset.green ${expected}} {dim is developed in this repo at ${shortPath}}`,
@@ -108,7 +111,7 @@ export function listMismatches(ctx: Syncpack.Ctx): Syncpack.Ctx {
108111

109112
function logHighestVersionMismatch(instanceGroup: InstanceGroup) {
110113
const name = instanceGroup.name;
111-
const expected = instanceGroup.getExpectedVersion();
114+
const expected = R.getExn(instanceGroup.getExpectedVersion());
112115
log.invalid(
113116
name,
114117
chalk`{reset.green ${expected}} {dim is the highest valid semver version in use}`,

src/bin-list/list.ts

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
import { O, pipe, R } from '@mobily/ts-belt';
12
import chalk from 'chalk';
23
import { ICON } from '../constants';
4+
import { $R } from '../get-context/$R';
35
import type { InstanceGroup } from '../get-context/get-groups/version-group/instance-group';
46
import * as log from '../lib/log';
57
import type { Syncpack } from '../types';
@@ -44,15 +46,21 @@ export function list(ctx: Syncpack.Ctx): Syncpack.Ctx {
4446
}
4547

4648
function logVersionMismatch(instanceGroup: InstanceGroup): void {
47-
console.log(
48-
chalk`{red ${ICON.cross} ${instanceGroup.name}} ${instanceGroup
49-
.getUniqueVersions()
50-
.map((version) =>
51-
version === instanceGroup.getExpectedVersion()
52-
? chalk.green(version)
53-
: chalk.red(version),
54-
)
55-
.join(chalk.dim(', '))}`,
49+
pipe(
50+
instanceGroup.getExpectedVersion(),
51+
R.tap((expectedVersion) => {
52+
const uniqueVersions = instanceGroup.getUniqueVersions();
53+
console.log(
54+
chalk`{red ${ICON.cross} ${instanceGroup.name}} ${uniqueVersions
55+
.map((version) =>
56+
version === expectedVersion
57+
? chalk.green(version)
58+
: chalk.red(version),
59+
)
60+
.join(chalk.dim(', '))}`,
61+
);
62+
}),
63+
$R.tapErrVerbose,
5664
);
5765
}
5866

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

7179
function logUnpinned(instanceGroup: InstanceGroup): void {
72-
const pinVersion = instanceGroup.versionGroup.getPinnedVersion();
80+
const pinVersion = O.getExn(instanceGroup.versionGroup.getPinnedVersion());
7381
console.log(
7482
chalk`{red ${ICON.cross} ${instanceGroup.name}} {dim.red is pinned to ${pinVersion} in this version group}`,
7583
);

src/get-context/get-all-instances.ts

Lines changed: 0 additions & 9 deletions
This file was deleted.

src/get-context/get-config/get-config.spec.ts

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
1+
import { R } from '@mobily/ts-belt';
12
import 'expect-more-jest';
23
import { getConfig } from '.';
34
import { mockDisk } from '../../../test/mock-disk';
5+
import type { BaseError } from '../../lib/error';
6+
import type { Syncpack } from '../../types';
47

58
describe('enabledTypes', () => {
69
const all = 'dev,overrides,peer,pnpmOverrides,prod,resolutions,workspace';
7-
const getNames = (config) => config.enabledTypes.map(({ name }) => name);
10+
11+
function getNames(config: R.Result<Syncpack.Config.Private, BaseError>) {
12+
return R.getExn(config).enabledTypes.map(({ name }) => name);
13+
}
814

915
it('enables all when nothing is set', () => {
1016
const disk = mockDisk();
@@ -53,110 +59,110 @@ describe('filter', () => {
5359
it('uses default when not set', () => {
5460
const disk = mockDisk();
5561
const config = getConfig(disk, {});
56-
expect(config.filter).toEqual('.');
62+
expect(R.getExn(config).filter).toEqual('.');
5763
});
5864

5965
it('uses CLI value when set', () => {
6066
const disk = mockDisk();
6167
const config = getConfig(disk, { filter: 'foo' });
62-
expect(config.filter).toEqual('foo');
68+
expect(R.getExn(config).filter).toEqual('foo');
6369
});
6470

6571
it('uses config value when set', () => {
6672
const disk = mockDisk();
6773
disk.readConfigFileSync.mockReturnValue({ filter: 'bar' });
6874
const config = getConfig(disk, {});
69-
expect(config.filter).toEqual('bar');
75+
expect(R.getExn(config).filter).toEqual('bar');
7076
});
7177

7278
it('uses CLI value when config and CLI are set', () => {
7379
const disk = mockDisk();
7480
disk.readConfigFileSync.mockReturnValue({ filter: 'bar' });
7581
const config = getConfig(disk, { filter: 'foo' });
76-
expect(config.filter).toEqual('foo');
82+
expect(R.getExn(config).filter).toEqual('foo');
7783
});
7884
});
7985

8086
describe('indent', () => {
8187
it('uses default when not set', () => {
8288
const disk = mockDisk();
8389
const config = getConfig(disk, {});
84-
expect(config.indent).toEqual(' ');
90+
expect(R.getExn(config).indent).toEqual(' ');
8591
});
8692

8793
it('uses CLI value when set', () => {
8894
const disk = mockDisk();
8995
const config = getConfig(disk, { indent: '\t' });
90-
expect(config.indent).toEqual('\t');
96+
expect(R.getExn(config).indent).toEqual('\t');
9197
});
9298

9399
it('uses config value when set', () => {
94100
const disk = mockDisk();
95101
disk.readConfigFileSync.mockReturnValue({ indent: '\t' });
96102
const config = getConfig(disk, {});
97-
expect(config.indent).toEqual('\t');
103+
expect(R.getExn(config).indent).toEqual('\t');
98104
});
99105

100106
it('uses CLI value when config and CLI are set', () => {
101107
const disk = mockDisk();
102108
disk.readConfigFileSync.mockReturnValue({ indent: '\t' });
103109
const config = getConfig(disk, { indent: ' ' });
104-
expect(config.indent).toEqual(' ');
110+
expect(R.getExn(config).indent).toEqual(' ');
105111
});
106112
});
107113

108114
describe('semverRange', () => {
109115
it('uses default when not set', () => {
110116
const disk = mockDisk();
111117
const config = getConfig(disk, {});
112-
expect(config.semverRange).toEqual('');
118+
expect(R.getExn(config).semverRange).toEqual('');
113119
});
114120

115121
it('uses CLI value when set', () => {
116122
const disk = mockDisk();
117123
const config = getConfig(disk, { semverRange: '^' });
118-
expect(config.semverRange).toEqual('^');
124+
expect(R.getExn(config).semverRange).toEqual('^');
119125
});
120126

121127
it('uses config value when set', () => {
122128
const disk = mockDisk();
123129
disk.readConfigFileSync.mockReturnValue({ semverRange: '~' });
124130
const config = getConfig(disk, {});
125-
expect(config.semverRange).toEqual('~');
131+
expect(R.getExn(config).semverRange).toEqual('~');
126132
});
127133

128134
it('uses CLI value when config and CLI are set', () => {
129135
const disk = mockDisk();
130136
disk.readConfigFileSync.mockReturnValue({ semverRange: '^' });
131137
const config = getConfig(disk, { semverRange: '*' });
132-
expect(config.semverRange).toEqual('*');
138+
expect(R.getExn(config).semverRange).toEqual('*');
133139
});
134140
});
135141

136142
describe('source', () => {
137143
it('uses default when not set', () => {
138144
const disk = mockDisk();
139145
const config = getConfig(disk, {});
140-
expect(config.source).toEqual([]);
146+
expect(R.getExn(config).source).toEqual([]);
141147
});
142148

143149
it('uses CLI value when set', () => {
144150
const disk = mockDisk();
145151
const config = getConfig(disk, { source: ['apps/*'] });
146-
expect(config.source).toEqual(['apps/*']);
152+
expect(R.getExn(config).source).toEqual(['apps/*']);
147153
});
148154

149155
it('uses config value when set', () => {
150156
const disk = mockDisk();
151157
disk.readConfigFileSync.mockReturnValue({ source: ['projects/*'] });
152158
const config = getConfig(disk, {});
153-
expect(config.source).toEqual(['projects/*']);
159+
expect(R.getExn(config).source).toEqual(['projects/*']);
154160
});
155161

156162
it('uses CLI value when config and CLI are set', () => {
157163
const disk = mockDisk();
158164
disk.readConfigFileSync.mockReturnValue({ source: ['projects/*'] });
159165
const config = getConfig(disk, { source: ['apps/*'] });
160-
expect(config.source).toEqual(['apps/*']);
166+
expect(R.getExn(config).source).toEqual(['apps/*']);
161167
});
162168
});

src/get-context/get-config/index.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import { pipe, R } from '@mobily/ts-belt';
12
import type { Disk } from '../../lib/disk';
3+
import { BaseError } from '../../lib/error';
24
import { verbose } from '../../lib/log';
35
import type { Syncpack } from '../../types';
46
import { getCoreTypes } from './get-core-types';
@@ -10,10 +12,21 @@ import * as ConfigSchema from './schema';
1012
* Take all configuration from the command line and config file, combine it, and
1113
* set defaults for anything which hasn't been defined.
1214
*/
13-
export const getConfig = (
15+
export function getConfig(
1416
disk: Disk,
1517
fromCli: Partial<Syncpack.Config.Cli>,
16-
): Syncpack.Config.Private => {
18+
): R.Result<Syncpack.Config.Private, BaseError> {
19+
const ERR_READING_CONFIG = 'Error reading config';
20+
return pipe(
21+
R.fromExecution(() => unSafeGetConfig(disk, fromCli)),
22+
R.mapError(BaseError.map(ERR_READING_CONFIG)),
23+
);
24+
}
25+
26+
function unSafeGetConfig(
27+
disk: Disk,
28+
fromCli: Partial<Syncpack.Config.Cli>,
29+
): Syncpack.Config.Private {
1730
verbose('cli arguments:', fromCli);
1831

1932
const fromRcFile = disk.readConfigFileSync(fromCli.configPath);
@@ -71,4 +84,4 @@ export const getConfig = (
7184
if (typeof (fromRcFile as any)[name] !== 'undefined')
7285
return (fromRcFile as Syncpack.Config.Public)[name];
7386
}
74-
};
87+
}

src/get-context/get-context.spec.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,29 +9,31 @@ describe('getContext', () => {
99
it('uses defaults when no CLI options or config are set', () => {
1010
const disk = mockDisk();
1111
expect(getContext({}, disk)).toHaveProperty(
12-
'source',
12+
'config.source',
1313
DEFAULT_CONFIG.source,
1414
);
1515
});
1616

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

2323
it('uses value from CLI when config and CLI options are set', () => {
2424
const disk = mockDisk();
2525
disk.readConfigFileSync.mockReturnValue({ source: ['foo'] });
26-
expect(getContext({ source: ['bar'] }, disk)).toHaveProperty('source', [
27-
'bar',
28-
]);
26+
expect(getContext({ source: ['bar'] }, disk)).toHaveProperty(
27+
'config.source',
28+
['bar'],
29+
);
2930
});
3031

3132
it('combines defaults, values from CLI options, and config', () => {
3233
const disk = mockDisk();
3334
disk.readConfigFileSync.mockReturnValue({ source: ['foo'] });
34-
expect(getContext({ indent: ' ' }, disk)).toEqual(
35+
expect(getContext({ indent: ' ' }, disk)).toHaveProperty(
36+
'config',
3537
expect.objectContaining({
3638
semverRange: '',
3739
source: ['foo'],

src/get-context/get-groups/base-group.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ export class BaseGroup<
4949
}
5050

5151
/** Add this instance to this group */
52-
add(instance: Instance) {
52+
add(instance: Instance): void {
5353
if (!this.instancesByName[instance.name]) {
5454
this.instancesByName[instance.name] = [];
5555
}

0 commit comments

Comments
 (0)