syncpack 12.3.3 → 13.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -2
- package/dist/bin-fix-mismatches/fix-mismatches.d.ts +2 -2
- package/dist/bin-fix-mismatches/fix-mismatches.js +13 -8
- package/dist/bin-fix-mismatches/index.js +1 -32
- package/dist/bin-format/format.d.ts +2 -2
- package/dist/bin-format/format.js +10 -7
- package/dist/bin-format/index.js +1 -26
- package/dist/bin-lint/index.js +1 -22
- package/dist/bin-lint/lint.d.ts +1 -1
- package/dist/bin-lint/lint.js +23 -23
- package/dist/bin-lint-semver-ranges/index.js +1 -36
- package/dist/bin-lint-semver-ranges/lint-semver-ranges.d.ts +2 -2
- package/dist/bin-lint-semver-ranges/lint-semver-ranges.js +5 -4
- package/dist/bin-list/index.js +1 -31
- package/dist/bin-list/list.d.ts +1 -1
- package/dist/bin-list/list.js +9 -5
- package/dist/bin-list-mismatches/index.js +1 -31
- package/dist/bin-list-mismatches/list-mismatches.d.ts +2 -2
- package/dist/bin-list-mismatches/list-mismatches.js +15 -9
- package/dist/bin-prompt/index.js +1 -33
- package/dist/bin-prompt/prompt.d.ts +2 -2
- package/dist/bin-prompt/prompt.js +13 -11
- package/dist/bin-set-semver-ranges/index.js +1 -38
- package/dist/bin-set-semver-ranges/set-semver-ranges.d.ts +2 -2
- package/dist/bin-set-semver-ranges/set-semver-ranges.js +8 -5
- package/dist/bin-update/effects.d.ts +1 -1
- package/dist/bin-update/effects.js +46 -31
- package/dist/bin-update/index.js +1 -31
- package/dist/bin-update/update.d.ts +1 -1
- package/dist/bin-update/update.js +4 -4
- package/dist/bin.js +3 -3
- package/dist/config/get-custom-types.js +20 -13
- package/dist/config/get-enabled-types.js +27 -12
- package/dist/config/get-sort-exports.js +2 -1
- package/dist/config/types.d.ts +2 -0
- package/dist/error-handlers/default-error-handlers.js +2 -2
- package/dist/get-context/index.d.ts +2 -2
- package/dist/get-context/index.js +1 -1
- package/dist/get-instances/index.js +6 -6
- package/dist/get-instances/instance.js +6 -2
- package/dist/get-package-json-files/get-patterns/get-lerna-patterns.js +4 -2
- package/dist/get-package-json-files/get-patterns/get-pnpm-patterns.d.ts +1 -1
- package/dist/get-package-json-files/get-patterns/get-pnpm-patterns.js +2 -2
- package/dist/get-package-json-files/get-patterns/get-yarn-patterns.js +2 -2
- package/dist/get-package-json-files/get-patterns/index.js +3 -3
- package/dist/get-package-json-files/index.js +1 -1
- package/dist/get-package-json-files/package-json-file.d.ts +7 -0
- package/dist/get-package-json-files/package-json-file.js +18 -3
- package/dist/guards/can-add-to-group.js +19 -13
- package/dist/guards/is-semver.js +3 -1
- package/dist/io/ask-for-choice.js +3 -3
- package/dist/io/ask-for-input.js +2 -2
- package/dist/io/exit-if-invalid.js +1 -1
- package/dist/io/glob-sync.js +1 -1
- package/dist/io/index.d.ts +1 -2
- package/dist/io/index.js +1 -1
- package/dist/io/read-config-file.js +5 -3
- package/dist/io/read-file-sync.js +1 -1
- package/dist/io/read-json-file-sync.d.ts +2 -1
- package/dist/io/read-json-file-sync.js +9 -5
- package/dist/io/read-yaml-file-sync.js +1 -1
- package/dist/io/{to-json.d.ts → to-formatted-json.d.ts} +1 -1
- package/dist/io/{to-json.js → to-formatted-json.js} +11 -8
- package/dist/io/write-file-sync.js +2 -2
- package/dist/io/write-if-changed.js +3 -4
- package/dist/lib/format-repository-url.js +8 -5
- package/dist/lib/get-group-header.js +3 -1
- package/dist/lib/get.js +20 -12
- package/dist/lib/ring-buffer.js +1 -1
- package/dist/lib/set-semver-range.js +8 -4
- package/dist/lib/with-logger.js +6 -6
- package/dist/option.js +4 -1
- package/dist/schema.json +3 -0
- package/dist/semver-group/create-semver-groups.js +7 -3
- package/dist/semver-group/disabled.js +1 -1
- package/dist/semver-group/filtered-out.js +1 -1
- package/dist/semver-group/ignored.js +1 -1
- package/dist/semver-group/with-range.js +3 -3
- package/dist/specifier/alias.js +3 -2
- package/dist/specifier/delete.d.ts +2 -2
- package/dist/specifier/exact.js +1 -1
- package/dist/specifier/hosted-git.js +5 -3
- package/dist/specifier/index.js +27 -15
- package/dist/specifier/latest.js +1 -1
- package/dist/specifier/lib/parse-specifier.js +3 -1
- package/dist/specifier/range.js +1 -1
- package/dist/specifier/workspace-protocol.js +2 -1
- package/dist/strategy/lib/get-non-empty-string-prop.js +1 -1
- package/dist/strategy/name-and-version-props.js +7 -7
- package/dist/strategy/named-version-string.js +11 -8
- package/dist/strategy/unnamed-version-string.js +6 -6
- package/dist/strategy/versions-by-name.js +4 -2
- package/dist/version-group/banned.js +1 -1
- package/dist/version-group/create-version-groups.js +11 -7
- package/dist/version-group/filtered-out.js +1 -1
- package/dist/version-group/ignored.js +1 -1
- package/dist/version-group/lib/get-preferred-version.js +22 -13
- package/dist/version-group/lib/get-range-score.js +3 -1
- package/dist/version-group/pinned.js +2 -2
- package/dist/version-group/same-range.js +6 -6
- package/dist/version-group/snapped-to.js +7 -5
- package/dist/version-group/standard.js +19 -16
- package/package.json +41 -33
|
@@ -16,9 +16,9 @@ export function getEnabledTypes({ cli, rcFile, }) {
|
|
|
16
16
|
return pipe(
|
|
17
17
|
// Look for dependency types defined using the old `{ prod: true }` syntax
|
|
18
18
|
// deprecated in syncpack@9.0.0
|
|
19
|
-
Effect.succeed(INTERNAL_TYPES.filter(
|
|
19
|
+
Effect.succeed(INTERNAL_TYPES.filter(key => isBoolean(rcFile[key]))),
|
|
20
20
|
// Short-circuit and quit if deprecated config is used
|
|
21
|
-
Effect.flatMap(
|
|
21
|
+
Effect.flatMap(deprecatedTypeProps => deprecatedTypeProps.length > 0
|
|
22
22
|
? Effect.fail(new DeprecatedTypesError({ types: deprecatedTypeProps }))
|
|
23
23
|
: Effect.void), Effect.flatMap(() => pipe(Effect.Do,
|
|
24
24
|
// Get index of every available strategy, keyed by their names as
|
|
@@ -30,13 +30,28 @@ export function getEnabledTypes({ cli, rcFile, }) {
|
|
|
30
30
|
// Combine them with the default/internal dependency types
|
|
31
31
|
Effect.map((customTypes) => Object.fromEntries([
|
|
32
32
|
['dev', new VersionsByNameStrategy('dev', 'devDependencies')],
|
|
33
|
-
[
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
[
|
|
34
|
+
'local',
|
|
35
|
+
new NameAndVersionPropsStrategy('local', 'version', 'name'),
|
|
36
|
+
],
|
|
37
|
+
[
|
|
38
|
+
'overrides',
|
|
39
|
+
new VersionsByNameStrategy('overrides', 'overrides'),
|
|
40
|
+
],
|
|
41
|
+
[
|
|
42
|
+
'peer',
|
|
43
|
+
new VersionsByNameStrategy('peer', 'peerDependencies'),
|
|
44
|
+
],
|
|
45
|
+
[
|
|
46
|
+
'pnpmOverrides',
|
|
47
|
+
new VersionsByNameStrategy('pnpmOverrides', 'pnpm.overrides'),
|
|
48
|
+
],
|
|
37
49
|
['prod', new VersionsByNameStrategy('prod', 'dependencies')],
|
|
38
|
-
[
|
|
39
|
-
|
|
50
|
+
[
|
|
51
|
+
'resolutions',
|
|
52
|
+
new VersionsByNameStrategy('resolutions', 'resolutions'),
|
|
53
|
+
],
|
|
54
|
+
...customTypes.map(type => [type.name, type]),
|
|
40
55
|
])))),
|
|
41
56
|
// The names of every available strategy
|
|
42
57
|
Effect.bind('allStrategyNames', ({ allStrategiesByName }) => Effect.succeed(Object.keys(allStrategiesByName))),
|
|
@@ -61,7 +76,7 @@ export function getEnabledTypes({ cli, rcFile, }) {
|
|
|
61
76
|
strategyNamesByStatus.provided.join('') === '**') {
|
|
62
77
|
return Effect.succeed(allStrategyNames.map(getStrategyByName));
|
|
63
78
|
}
|
|
64
|
-
strategyNamesByStatus.provided.forEach(
|
|
79
|
+
strategyNamesByStatus.provided.forEach(name => {
|
|
65
80
|
if (name.startsWith('!')) {
|
|
66
81
|
strategyNamesByStatus.negative.push(name.replace('!', ''));
|
|
67
82
|
}
|
|
@@ -70,14 +85,14 @@ export function getEnabledTypes({ cli, rcFile, }) {
|
|
|
70
85
|
}
|
|
71
86
|
});
|
|
72
87
|
if (isNonEmptyArray(strategyNamesByStatus.negative)) {
|
|
73
|
-
allStrategyNames.forEach(
|
|
88
|
+
allStrategyNames.forEach(name => {
|
|
74
89
|
if (!strategyNamesByStatus.negative.includes(name)) {
|
|
75
90
|
strategyNamesByStatus.enabled.push(name);
|
|
76
91
|
}
|
|
77
92
|
});
|
|
78
93
|
}
|
|
79
94
|
if (isNonEmptyArray(strategyNamesByStatus.positive)) {
|
|
80
|
-
strategyNamesByStatus.positive.forEach(
|
|
95
|
+
strategyNamesByStatus.positive.forEach(name => {
|
|
81
96
|
if (!strategyNamesByStatus.enabled.includes(name)) {
|
|
82
97
|
strategyNamesByStatus.enabled.push(name);
|
|
83
98
|
}
|
|
@@ -90,5 +105,5 @@ export function getEnabledTypes({ cli, rcFile, }) {
|
|
|
90
105
|
function getStrategyByName(type) {
|
|
91
106
|
return allStrategiesByName[type];
|
|
92
107
|
}
|
|
93
|
-
}), Effect.tap(
|
|
108
|
+
}), Effect.tap(enabledTypes => Effect.logDebug(`enabled dependency types determined to be: ${JSON.stringify(enabledTypes)}`)));
|
|
94
109
|
}
|
|
@@ -2,7 +2,8 @@ import { isArrayOfStrings } from 'tightrope/guard/is-array-of-strings.js';
|
|
|
2
2
|
import { isEmptyArray } from 'tightrope/guard/is-empty-array.js';
|
|
3
3
|
import { DEFAULT_CONFIG } from '../constants.js';
|
|
4
4
|
export function getSortExports({ rcFile }) {
|
|
5
|
-
return isArrayOfStrings(rcFile.sortExports) ||
|
|
5
|
+
return isArrayOfStrings(rcFile.sortExports) ||
|
|
6
|
+
isEmptyArray(rcFile.sortExports)
|
|
6
7
|
? rcFile.sortExports
|
|
7
8
|
: DEFAULT_CONFIG.sortExports;
|
|
8
9
|
}
|
package/dist/config/types.d.ts
CHANGED
|
@@ -92,6 +92,8 @@ export interface CliConfig {
|
|
|
92
92
|
readonly types: string;
|
|
93
93
|
}
|
|
94
94
|
export interface RcConfig {
|
|
95
|
+
/** @see https://jamiemason.github.io/syncpack/integrations/json-schema */
|
|
96
|
+
$schema?: string;
|
|
95
97
|
/** @see https://jamiemason.github.io/syncpack/config/custom-types */
|
|
96
98
|
customTypes: Record<string, CustomTypeConfig.Any>;
|
|
97
99
|
/** @see https://jamiemason.github.io/syncpack/config/dependency-types */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { EOL } from 'node:os';
|
|
1
2
|
import chalk from 'chalk';
|
|
2
3
|
import { Effect } from 'effect';
|
|
3
|
-
import { EOL } from 'os';
|
|
4
4
|
export const defaultErrorHandlers = {
|
|
5
5
|
// getContext
|
|
6
6
|
GlobError(err) {
|
|
@@ -13,7 +13,7 @@ export const defaultErrorHandlers = {
|
|
|
13
13
|
return Effect.logError([
|
|
14
14
|
chalk.red('An error was found when parsing a JSON file'),
|
|
15
15
|
chalk.red(' File:', err.filePath),
|
|
16
|
-
chalk.red(' Error:', err.
|
|
16
|
+
chalk.red(' Error:', err.errors),
|
|
17
17
|
].join(EOL));
|
|
18
18
|
},
|
|
19
19
|
NoSourcesFoundError(err) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Effect } from 'effect';
|
|
2
2
|
import type { O } from 'ts-toolbelt';
|
|
3
|
-
import {
|
|
3
|
+
import type { CliConfig, RcConfig } from '../config/types.js';
|
|
4
4
|
import type { ErrorHandlers } from '../error-handlers/default-error-handlers.js';
|
|
5
5
|
import type { PackageJsonFile } from '../get-package-json-files/package-json-file.js';
|
|
6
6
|
import type { Io } from '../io/index.js';
|
|
@@ -19,5 +19,5 @@ interface Input {
|
|
|
19
19
|
cli: Partial<CliConfig>;
|
|
20
20
|
errorHandlers: ErrorHandlers;
|
|
21
21
|
}
|
|
22
|
-
export declare function getContext({ io, cli, errorHandlers }: Input): Effect.Effect<Ctx>;
|
|
22
|
+
export declare function getContext({ io, cli, errorHandlers, }: Input): Effect.Effect<Ctx>;
|
|
23
23
|
export {};
|
|
@@ -2,7 +2,7 @@ import { Effect, flow, pipe } from 'effect';
|
|
|
2
2
|
import { getPackageJsonFiles } from '../get-package-json-files/index.js';
|
|
3
3
|
import { readConfigFile } from '../io/read-config-file.js';
|
|
4
4
|
import { keyBy } from './lib/key-by.js';
|
|
5
|
-
export function getContext({ io, cli, errorHandlers }) {
|
|
5
|
+
export function getContext({ io, cli, errorHandlers, }) {
|
|
6
6
|
const exitOnError = Effect.flatMap(() => Effect.failSync(() => io.process.exit(1)));
|
|
7
7
|
return pipe(Effect.Do, Effect.bind('rcFile', () => readConfigFile(io, cli.configPath)), Effect.bind('packageJsonFiles', ({ rcFile }) => getPackageJsonFiles(io, { cli, rcFile })), Effect.map(({ rcFile, packageJsonFiles }) => ({
|
|
8
8
|
config: { cli, rcFile },
|
|
@@ -6,22 +6,22 @@ import { createSemverGroups } from '../semver-group/create-semver-groups.js';
|
|
|
6
6
|
import { createVersionGroups } from '../version-group/create-version-groups.js';
|
|
7
7
|
export function getInstances(ctx, io, errorHandlers) {
|
|
8
8
|
const exitOnError = Effect.flatMap(() => Effect.failSync(() => io.process.exit(1)));
|
|
9
|
-
return pipe(Effect.Do, Effect.bind('enabledTypes', () => getEnabledTypes(ctx.config)), Effect.bind('semverGroups', () => createSemverGroups(ctx)), Effect.bind('versionGroups', () => createVersionGroups(ctx)), Effect.bind('instances',
|
|
9
|
+
return pipe(Effect.Do, Effect.bind('enabledTypes', () => getEnabledTypes(ctx.config)), Effect.bind('semverGroups', () => createSemverGroups(ctx)), Effect.bind('versionGroups', () => createVersionGroups(ctx)), Effect.bind('instances', acc => pipe(ctx.packageJsonFiles, Effect.forEach(file => file.getInstances(acc.enabledTypes)), Effect.map(instancesByFile => instancesByFile.flat()))), Effect.tap(({ instances, semverGroups, versionGroups }) => Effect.sync(() => {
|
|
10
10
|
for (const instance of instances) {
|
|
11
11
|
// assign each instance to its semver group, first match wins
|
|
12
|
-
|
|
12
|
+
for (const group of semverGroups) {
|
|
13
13
|
if (canAddToGroup(ctx.packageJsonFilesByName, group, instance)) {
|
|
14
14
|
instance.semverGroup = group;
|
|
15
15
|
group.instances.push(instance);
|
|
16
|
-
break
|
|
16
|
+
break;
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
19
|
// assign each instance to its version group, first match wins
|
|
20
|
-
|
|
20
|
+
for (const group of versionGroups) {
|
|
21
21
|
if (canAddToGroup(ctx.packageJsonFilesByName, group, instance)) {
|
|
22
22
|
instance.versionGroup = group;
|
|
23
23
|
group.instances.push(instance);
|
|
24
|
-
break
|
|
24
|
+
break;
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
}
|
|
@@ -37,6 +37,6 @@ export function getInstances(ctx, io, errorHandlers) {
|
|
|
37
37
|
VersionGroupConfigError: flow(errorHandlers.VersionGroupConfigError, exitOnError),
|
|
38
38
|
}));
|
|
39
39
|
function getSortedAndFiltered(groups) {
|
|
40
|
-
return groups.filter(
|
|
40
|
+
return groups.filter(group => group.instances.sort(sortByName).length > 0);
|
|
41
41
|
}
|
|
42
42
|
}
|
|
@@ -16,7 +16,8 @@ export class Instance {
|
|
|
16
16
|
versionGroup;
|
|
17
17
|
constructor(name, rawSpecifier, packageJsonFile, strategy) {
|
|
18
18
|
this.name = name;
|
|
19
|
-
this.pkgName =
|
|
19
|
+
this.pkgName =
|
|
20
|
+
packageJsonFile.jsonFile.contents.name || 'PACKAGE_JSON_HAS_NO_NAME';
|
|
20
21
|
this.packageJsonFile = packageJsonFile;
|
|
21
22
|
this.strategy = strategy;
|
|
22
23
|
this.semverGroup = null;
|
|
@@ -26,6 +27,9 @@ export class Instance {
|
|
|
26
27
|
/** Mutate the package.json file in memory with the latest version specifier */
|
|
27
28
|
write(rawSpecifier) {
|
|
28
29
|
this.rawSpecifier = Specifier.create(this, rawSpecifier);
|
|
29
|
-
return this.strategy.write(this.packageJsonFile, [
|
|
30
|
+
return this.strategy.write(this.packageJsonFile, [
|
|
31
|
+
this.name,
|
|
32
|
+
this.rawSpecifier.raw,
|
|
33
|
+
]);
|
|
30
34
|
}
|
|
31
35
|
}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
1
2
|
import { Effect, Option as O, pipe } from 'effect';
|
|
2
|
-
import { join } from 'path';
|
|
3
3
|
import { isArrayOfStrings } from 'tightrope/guard/is-array-of-strings.js';
|
|
4
4
|
import { readJsonFileSync } from '../../io/read-json-file-sync.js';
|
|
5
5
|
export function getLernaPatterns(io) {
|
|
6
|
-
return pipe(readJsonFileSync(io, join(io.process.cwd(), 'lerna.json')), Effect.map(
|
|
6
|
+
return pipe(readJsonFileSync(io, join(io.process.cwd(), 'lerna.json')), Effect.map(file => isArrayOfStrings(file.contents.packages)
|
|
7
|
+
? O.some(file.contents.packages)
|
|
8
|
+
: O.none()), Effect.catchTags({
|
|
7
9
|
ReadFileError: () => Effect.succeed(O.none()),
|
|
8
10
|
JsonParseError: () => Effect.succeed(O.none()),
|
|
9
11
|
}));
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
1
2
|
import { Effect, Option as O, pipe } from 'effect';
|
|
2
|
-
import { join } from 'path';
|
|
3
3
|
import { isArrayOfStrings } from 'tightrope/guard/is-array-of-strings.js';
|
|
4
4
|
import { readYamlFileSync } from '../../io/read-yaml-file-sync.js';
|
|
5
5
|
export function getPnpmPatterns(io) {
|
|
@@ -8,7 +8,7 @@ export function getPnpmPatterns(io) {
|
|
|
8
8
|
// - "packages/**"
|
|
9
9
|
// - "components/**"
|
|
10
10
|
// - "!**/test/**"
|
|
11
|
-
readYamlFileSync(io, join(io.process.cwd(), 'pnpm-workspace.yaml')), Effect.map(
|
|
11
|
+
readYamlFileSync(io, join(io.process.cwd(), 'pnpm-workspace.yaml')), Effect.map(file => isArrayOfStrings(file?.packages) ? O.some(file.packages) : O.none()), Effect.catchTags({
|
|
12
12
|
ReadYamlFileError: () => Effect.succeed(O.none()),
|
|
13
13
|
}));
|
|
14
14
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
1
2
|
import { Effect, Option as O, pipe } from 'effect';
|
|
2
|
-
import { join } from 'path';
|
|
3
3
|
import { isArrayOfStrings } from 'tightrope/guard/is-array-of-strings.js';
|
|
4
4
|
import { isNonEmptyObject } from 'tightrope/guard/is-non-empty-object.js';
|
|
5
5
|
import { readJsonFileSync } from '../../io/read-json-file-sync.js';
|
|
6
6
|
export function getYarnPatterns(io) {
|
|
7
|
-
return pipe(readJsonFileSync(io, join(io.process.cwd(), 'package.json')), Effect.map(
|
|
7
|
+
return pipe(readJsonFileSync(io, join(io.process.cwd(), 'package.json')), Effect.map(file => isNonEmptyObject(file.contents.workspaces) &&
|
|
8
8
|
isArrayOfStrings(file.contents.workspaces.packages)
|
|
9
9
|
? O.some(file.contents.workspaces.packages)
|
|
10
10
|
: isArrayOfStrings(file.contents.workspaces)
|
|
@@ -10,14 +10,14 @@ import { getYarnPatterns } from './get-yarn-patterns.js';
|
|
|
10
10
|
* this monorepo.
|
|
11
11
|
*/
|
|
12
12
|
export function getPatterns(io, config) {
|
|
13
|
-
return pipe(getCliPatterns(), Effect.flatMap(
|
|
13
|
+
return pipe(getCliPatterns(), Effect.flatMap(opt => O.isSome(opt) ? Effect.succeed(opt) : getWorkspacePatterns()), Effect.map(O.map(limitToPackageJson)), Effect.map(O.getOrElse(() => [...DEFAULT_CONFIG.source])));
|
|
14
14
|
function getCliPatterns() {
|
|
15
15
|
return pipe(O.some(getSource(config)), O.filter(isArrayOfStrings), Effect.succeed);
|
|
16
16
|
}
|
|
17
17
|
function getWorkspacePatterns() {
|
|
18
|
-
return pipe(getYarnPatterns(io), Effect.flatMap(
|
|
18
|
+
return pipe(getYarnPatterns(io), Effect.flatMap(opt => O.isSome(opt) ? Effect.succeed(opt) : getPnpmPatterns(io)), Effect.flatMap(opt => O.isSome(opt) ? Effect.succeed(opt) : getLernaPatterns(io)), Effect.map(O.map(patterns => ['package.json', ...patterns])));
|
|
19
19
|
}
|
|
20
20
|
function limitToPackageJson(patterns) {
|
|
21
|
-
return patterns.map(
|
|
21
|
+
return patterns.map(pattern => pattern.includes('package.json') ? pattern : `${pattern}/package.json`);
|
|
22
22
|
}
|
|
23
23
|
}
|
|
@@ -4,5 +4,5 @@ import { getFilePaths } from './get-file-paths.js';
|
|
|
4
4
|
import { PackageJsonFile } from './package-json-file.js';
|
|
5
5
|
/** Create an API for every package.json file needed. */
|
|
6
6
|
export function getPackageJsonFiles(io, config) {
|
|
7
|
-
return pipe(getFilePaths(io, config), Effect.flatMap(
|
|
7
|
+
return pipe(getFilePaths(io, config), Effect.flatMap(filePaths => Effect.all(filePaths.map(filePath => readJsonFileSync(io, filePath)))), Effect.map(files => files.map(file => new PackageJsonFile(file, config))), Effect.tap(files => Effect.logDebug(`${files.length} package.json files found`)));
|
|
8
8
|
}
|
|
@@ -42,6 +42,13 @@ export declare class PackageJsonFile {
|
|
|
42
42
|
jsonFile: JsonFile<PackageJson>;
|
|
43
43
|
/** the .name property from the package.json file */
|
|
44
44
|
name: string | undefined;
|
|
45
|
+
/** the next package.json file contents after modification, with formatting preserved */
|
|
46
|
+
nextJson: string;
|
|
45
47
|
constructor(jsonFile: JsonFile<PackageJson>, config: Ctx['config']);
|
|
46
48
|
getInstances(enabledTypes: Strategy.Any[]): Effect.Effect<Instance[]>;
|
|
49
|
+
/**
|
|
50
|
+
* Apply an edit to the raw JSON string which will be written to disk. This string preserves the
|
|
51
|
+
* original formatting of the file.
|
|
52
|
+
*/
|
|
53
|
+
applyEdit(fullPath: string[], value: string | undefined): void;
|
|
47
54
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Effect, pipe } from 'effect';
|
|
2
|
+
import { applyEdits, modify } from 'jsonc-parser';
|
|
2
3
|
import { Instance } from '../get-instances/instance.js';
|
|
3
4
|
export class PackageJsonFile {
|
|
4
5
|
/** resolved configuration */
|
|
@@ -9,21 +10,35 @@ export class PackageJsonFile {
|
|
|
9
10
|
jsonFile;
|
|
10
11
|
/** the .name property from the package.json file */
|
|
11
12
|
name;
|
|
13
|
+
/** the next package.json file contents after modification, with formatting preserved */
|
|
14
|
+
nextJson;
|
|
12
15
|
constructor(jsonFile, config) {
|
|
13
16
|
this._instances = null;
|
|
14
17
|
this.config = config;
|
|
15
18
|
this.jsonFile = jsonFile;
|
|
16
19
|
this.name = jsonFile.contents.name;
|
|
20
|
+
this.nextJson = jsonFile.json;
|
|
17
21
|
}
|
|
18
22
|
getInstances(enabledTypes) {
|
|
19
23
|
if (!this._instances) {
|
|
20
|
-
return pipe(Effect.all(enabledTypes.map(
|
|
21
|
-
onSuccess:
|
|
24
|
+
return pipe(Effect.all(enabledTypes.map(strategy => pipe(strategy.read(this), Effect.map(entries => entries.map(([name, rawSpecifier]) => new Instance(name, rawSpecifier, this, strategy)))))), Effect.map(array => array.flat()), Effect.tapBoth({
|
|
25
|
+
onSuccess: instances => Effect.logDebug(`found ${instances.length} instances in <${this.jsonFile.shortPath}>`),
|
|
22
26
|
onFailure: () => Effect.logError(`failed to get instances from <${this.jsonFile.shortPath}>`),
|
|
23
|
-
}), Effect.catchAll(() => Effect.succeed([])), Effect.tap(
|
|
27
|
+
}), Effect.catchAll(() => Effect.succeed([])), Effect.tap(instances => Effect.sync(() => {
|
|
24
28
|
this._instances = instances;
|
|
25
29
|
})));
|
|
26
30
|
}
|
|
27
31
|
return Effect.succeed(this._instances);
|
|
28
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* Apply an edit to the raw JSON string which will be written to disk. This string preserves the
|
|
35
|
+
* original formatting of the file.
|
|
36
|
+
*/
|
|
37
|
+
applyEdit(fullPath, value) {
|
|
38
|
+
const edits = modify(this.nextJson, fullPath.map(parseNumericStrings), value, {});
|
|
39
|
+
this.nextJson = applyEdits(this.nextJson, edits);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function parseNumericStrings(key) {
|
|
43
|
+
return /[^0-9]/.test(key) ? key : Number(key);
|
|
29
44
|
}
|
|
@@ -10,19 +10,19 @@ export function canAddToGroup(packageJsonFilesByName, group, instance) {
|
|
|
10
10
|
}
|
|
11
11
|
function matchesDependencies(packageJsonFilesByName, group, dependencies, instance) {
|
|
12
12
|
// matches if not defined
|
|
13
|
-
if (!isNonEmptyArray(dependencies))
|
|
13
|
+
if (!isNonEmptyArray(dependencies)) {
|
|
14
14
|
return true;
|
|
15
|
-
|
|
15
|
+
}
|
|
16
|
+
return dependencies.some(pattern => (pattern === '$LOCAL' &&
|
|
16
17
|
instance.name in packageJsonFilesByName &&
|
|
17
|
-
((group.groupType === 'versionGroup' &&
|
|
18
|
-
|
|
18
|
+
((group.groupType === 'versionGroup' &&
|
|
19
|
+
instance.versionGroup === null) ||
|
|
20
|
+
(group.groupType === 'semverGroup' &&
|
|
21
|
+
instance.semverGroup === null))) ||
|
|
19
22
|
minimatch(instance.name, pattern));
|
|
20
23
|
}
|
|
21
24
|
function matchesPackages(packages, instance) {
|
|
22
|
-
|
|
23
|
-
if (!isNonEmptyArray(packages))
|
|
24
|
-
return true;
|
|
25
|
-
return packages.some((pattern) => minimatch(instance.pkgName, pattern));
|
|
25
|
+
return matchesKnownList(packages, instance.pkgName);
|
|
26
26
|
}
|
|
27
27
|
function matchesDependencyTypes(dependencyTypes, instance) {
|
|
28
28
|
return matchesKnownList(dependencyTypes, instance.strategy.name);
|
|
@@ -32,13 +32,15 @@ function matchesSpecifierTypes(specifierTypes, instance) {
|
|
|
32
32
|
}
|
|
33
33
|
function matchesKnownList(values, value) {
|
|
34
34
|
// matches if not defined
|
|
35
|
-
if (!isNonEmptyArray(values))
|
|
35
|
+
if (!isNonEmptyArray(values)) {
|
|
36
36
|
return true;
|
|
37
|
-
|
|
37
|
+
}
|
|
38
|
+
if (values.join('') === '**') {
|
|
38
39
|
return true;
|
|
40
|
+
}
|
|
39
41
|
const negative = [];
|
|
40
42
|
const positive = [];
|
|
41
|
-
values.forEach(
|
|
43
|
+
values.forEach(name => {
|
|
42
44
|
if (name.startsWith('!')) {
|
|
43
45
|
negative.push(name.replace('!', ''));
|
|
44
46
|
}
|
|
@@ -46,7 +48,11 @@ function matchesKnownList(values, value) {
|
|
|
46
48
|
positive.push(name);
|
|
47
49
|
}
|
|
48
50
|
});
|
|
49
|
-
if (isNonEmptyArray(negative) && !
|
|
51
|
+
if (isNonEmptyArray(negative) && !someMinimatch(value, negative)) {
|
|
50
52
|
return true;
|
|
51
|
-
|
|
53
|
+
}
|
|
54
|
+
return isNonEmptyArray(positive) && someMinimatch(value, positive);
|
|
55
|
+
}
|
|
56
|
+
function someMinimatch(value, patterns) {
|
|
57
|
+
return patterns.some(pattern => minimatch(value, pattern));
|
|
52
58
|
}
|
package/dist/guards/is-semver.js
CHANGED
|
@@ -9,5 +9,7 @@ export function isSemver(version) {
|
|
|
9
9
|
const minor = new RegExp(`^${range}${ints}${dot}${intsOrX}$`);
|
|
10
10
|
const patch = new RegExp(`^${range}${ints}${dot}${intsOrX}${dot}${intsOrX}$`);
|
|
11
11
|
return (isString(version) &&
|
|
12
|
-
(version.search(major) !== -1 ||
|
|
12
|
+
(version.search(major) !== -1 ||
|
|
13
|
+
version.search(minor) !== -1 ||
|
|
14
|
+
version.search(patch) !== -1));
|
|
13
15
|
}
|
|
@@ -3,7 +3,7 @@ import { IoTag } from './index.js';
|
|
|
3
3
|
class AskForChoiceError extends Data.TaggedClass('AskForChoiceError') {
|
|
4
4
|
}
|
|
5
5
|
export function askForChoice(opts) {
|
|
6
|
-
return pipe(IoTag, Effect.flatMap(
|
|
6
|
+
return pipe(IoTag, Effect.flatMap(io => Effect.tryPromise({
|
|
7
7
|
try: () => io.enquirer
|
|
8
8
|
.prompt({
|
|
9
9
|
type: 'select',
|
|
@@ -11,7 +11,7 @@ export function askForChoice(opts) {
|
|
|
11
11
|
message: opts.message,
|
|
12
12
|
choices: opts.choices,
|
|
13
13
|
})
|
|
14
|
-
.then(
|
|
15
|
-
catch:
|
|
14
|
+
.then(res => res.choice),
|
|
15
|
+
catch: err => new AskForChoiceError({ error: String(err) }),
|
|
16
16
|
})));
|
|
17
17
|
}
|
package/dist/io/ask-for-input.js
CHANGED
|
@@ -3,12 +3,12 @@ import { IoTag } from './index.js';
|
|
|
3
3
|
class AskForInputError extends Data.TaggedClass('AskForInputError') {
|
|
4
4
|
}
|
|
5
5
|
export function askForInput(opts) {
|
|
6
|
-
return pipe(IoTag, Effect.flatMap(
|
|
6
|
+
return pipe(IoTag, Effect.flatMap(io => Effect.tryPromise({
|
|
7
7
|
try: () => io.enquirer.prompt({
|
|
8
8
|
name: 'version',
|
|
9
9
|
type: 'input',
|
|
10
10
|
message: opts.message,
|
|
11
11
|
}),
|
|
12
|
-
catch:
|
|
12
|
+
catch: err => new AskForInputError({ error: String(err) }),
|
|
13
13
|
})));
|
|
14
14
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Effect, pipe } from 'effect';
|
|
2
2
|
import { IoTag } from './index.js';
|
|
3
3
|
export function exitIfInvalid(ctx) {
|
|
4
|
-
return pipe(IoTag, Effect.tap(
|
|
4
|
+
return pipe(IoTag, Effect.tap(io => Effect.sync(() => {
|
|
5
5
|
if (ctx.isInvalid) {
|
|
6
6
|
io.process.exit(1);
|
|
7
7
|
}
|
package/dist/io/glob-sync.js
CHANGED
package/dist/io/index.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
import fs from 'node:fs';
|
|
2
2
|
import { cosmiconfig } from 'cosmiconfig';
|
|
3
3
|
import { Context } from 'effect';
|
|
4
4
|
import enquirer from 'enquirer';
|
|
5
|
-
import fs from 'fs';
|
|
6
5
|
import { globbySync } from 'globby';
|
|
7
6
|
import { sync as readYamlFileSync } from 'read-yaml-file';
|
|
8
7
|
export interface Io {
|
package/dist/io/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
1
2
|
import { cosmiconfig } from 'cosmiconfig';
|
|
2
3
|
import { Context } from 'effect';
|
|
3
4
|
import enquirer from 'enquirer';
|
|
4
|
-
import fs from 'fs';
|
|
5
5
|
import { globbySync } from 'globby';
|
|
6
6
|
import { sync as readYamlFileSync } from 'read-yaml-file';
|
|
7
7
|
export const IoTag = Context.GenericTag('@services/IoTag');
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
1
2
|
import { Effect, Option, pipe } from 'effect';
|
|
2
|
-
import { join } from 'path';
|
|
3
3
|
import { isNonEmptyObject } from 'tightrope/guard/is-non-empty-object.js';
|
|
4
4
|
import { readJsonFileSync } from './read-json-file-sync.js';
|
|
5
5
|
const getOptionOfNonEmptyObject = Option.liftPredicate((isNonEmptyObject));
|
|
6
6
|
export function readConfigFile(io, configPath) {
|
|
7
|
-
return pipe(Effect.try(() => io.cosmiconfig.cosmiconfig('syncpack')), Effect.flatMap(
|
|
7
|
+
return pipe(Effect.try(() => io.cosmiconfig.cosmiconfig('syncpack')), Effect.flatMap(client => Effect.tryPromise(() => configPath ? client.load(configPath) : client.search())), Effect.flatMap(result => result !== null
|
|
8
|
+
? getValueFromCosmiconfig(result)
|
|
9
|
+
: findConfigInPackageJson(io)), Effect.tap(config => Effect.logDebug(`config file found: ${JSON.stringify(config)}`)), Effect.tapError(() => Effect.logDebug('no config file found, will use defaults')), Effect.catchAll(() => Effect.succeed({})));
|
|
8
10
|
}
|
|
9
11
|
/**
|
|
10
12
|
* Look for a .config.syncpack property in the root package.json.
|
|
@@ -18,5 +20,5 @@ function findConfigInPackageJson(io) {
|
|
|
18
20
|
}
|
|
19
21
|
/** Extract the value from a successful search by cosmiconfig */
|
|
20
22
|
function getValueFromCosmiconfig(result) {
|
|
21
|
-
return pipe(Effect.succeed(result), Effect.tap(
|
|
23
|
+
return pipe(Effect.succeed(result), Effect.tap(result => Effect.logDebug(`cosmiconfig found ${result.filepath}`)), Effect.flatMap(result => getOptionOfNonEmptyObject(result.config)));
|
|
22
24
|
}
|
|
@@ -4,6 +4,6 @@ export class ReadFileError extends Data.TaggedClass('ReadFileError') {
|
|
|
4
4
|
export function readFileSync(io, filePath) {
|
|
5
5
|
return Effect.try({
|
|
6
6
|
try: () => io.fs.readFileSync(filePath, { encoding: 'utf8' }),
|
|
7
|
-
catch:
|
|
7
|
+
catch: err => new ReadFileError({ filePath, error: String(err) }),
|
|
8
8
|
});
|
|
9
9
|
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { Effect } from 'effect';
|
|
2
|
+
import { type ParseError } from 'jsonc-parser';
|
|
2
3
|
import type { Io } from './index.js';
|
|
3
4
|
import type { ReadFileError } from './read-file-sync.js';
|
|
4
5
|
declare const JsonParseError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => Readonly<A> & {
|
|
5
6
|
readonly _tag: "JsonParseError";
|
|
6
7
|
};
|
|
7
8
|
export declare class JsonParseError extends JsonParseError_base<{
|
|
8
|
-
readonly
|
|
9
|
+
readonly errors: ParseError[];
|
|
9
10
|
readonly filePath: string;
|
|
10
11
|
readonly json: string;
|
|
11
12
|
}> {
|
|
@@ -1,15 +1,19 @@
|
|
|
1
|
+
import { dirname, relative } from 'node:path';
|
|
1
2
|
import { Data, Effect, pipe } from 'effect';
|
|
2
|
-
import {
|
|
3
|
+
import { parse } from 'jsonc-parser';
|
|
3
4
|
import { readFileSync } from './read-file-sync.js';
|
|
4
5
|
export class JsonParseError extends Data.TaggedClass('JsonParseError') {
|
|
5
6
|
}
|
|
6
7
|
export class JsonFile extends Data.TaggedClass('JsonFile') {
|
|
7
8
|
}
|
|
8
9
|
export function readJsonFileSync(io, filePath) {
|
|
9
|
-
return pipe(Effect.Do, Effect.bind('json', () => readFileSync(io, filePath)), Effect.bind('contents', ({ json }) =>
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
return pipe(Effect.Do, Effect.bind('json', () => readFileSync(io, filePath)), Effect.bind('contents', ({ json }) => {
|
|
11
|
+
const errors = [];
|
|
12
|
+
const data = parse(json, errors);
|
|
13
|
+
return errors.length === 0
|
|
14
|
+
? Effect.succeed(data)
|
|
15
|
+
: Effect.fail(new JsonParseError({ errors, filePath, json }));
|
|
16
|
+
}), Effect.map(({ contents, json }) => new JsonFile({
|
|
13
17
|
contents,
|
|
14
18
|
dirPath: dirname(filePath),
|
|
15
19
|
filePath,
|
|
@@ -4,6 +4,6 @@ class ReadYamlFileError extends Data.TaggedClass('ReadYamlFileError') {
|
|
|
4
4
|
export function readYamlFileSync(io, filePath) {
|
|
5
5
|
return Effect.try({
|
|
6
6
|
try: () => io.readYamlFile.sync(filePath),
|
|
7
|
-
catch:
|
|
7
|
+
catch: err => new ReadYamlFileError({ filePath, error: String(err) }),
|
|
8
8
|
});
|
|
9
9
|
}
|
|
@@ -5,5 +5,5 @@ export declare const newlines: {
|
|
|
5
5
|
detect(source: string): Ending;
|
|
6
6
|
fix(source: string, lineEnding: Ending): string;
|
|
7
7
|
};
|
|
8
|
-
export declare function
|
|
8
|
+
export declare function toFormattedJson(ctx: Ctx, file: PackageJsonFile): string;
|
|
9
9
|
export {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EOL } from 'os';
|
|
1
|
+
import { EOL } from 'node:os';
|
|
2
2
|
import { getIndent } from '../config/get-indent.js';
|
|
3
3
|
const CR = '\r';
|
|
4
4
|
const CRLF = '\r\n';
|
|
@@ -8,22 +8,25 @@ export const newlines = {
|
|
|
8
8
|
const cr = source.split(CR).length;
|
|
9
9
|
const lf = source.split(LF).length;
|
|
10
10
|
const crlf = source.split(CRLF).length;
|
|
11
|
-
if (cr + lf === 0)
|
|
11
|
+
if (cr + lf === 0) {
|
|
12
12
|
return EOL;
|
|
13
|
-
|
|
13
|
+
}
|
|
14
|
+
if (crlf === cr && crlf === lf) {
|
|
14
15
|
return CRLF;
|
|
15
|
-
|
|
16
|
+
}
|
|
17
|
+
if (cr > lf) {
|
|
16
18
|
return CR;
|
|
19
|
+
}
|
|
17
20
|
return LF;
|
|
18
21
|
},
|
|
19
22
|
fix(source, lineEnding) {
|
|
20
23
|
return source.replace(/\r\n|\n|\r/g, lineEnding);
|
|
21
24
|
},
|
|
22
25
|
};
|
|
23
|
-
export function
|
|
26
|
+
export function toFormattedJson(ctx, file) {
|
|
24
27
|
const contents = file.jsonFile.contents;
|
|
25
28
|
const indent = getIndent(ctx.config);
|
|
26
|
-
const
|
|
27
|
-
const source = `${JSON.stringify(contents, null, indent)}${
|
|
28
|
-
return newlines.fix(source,
|
|
29
|
+
const eol = newlines.detect(file.jsonFile.json);
|
|
30
|
+
const source = `${JSON.stringify(contents, null, indent)}${eol}`;
|
|
31
|
+
return newlines.fix(source, eol);
|
|
29
32
|
}
|