@neurodevs/meta-node 0.17.0 → 0.17.2

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.
@@ -5,8 +5,10 @@ export default class NpmReleasePropagatorTest extends AbstractPackageTest {
5
5
  protected static beforeEach(): Promise<void>;
6
6
  protected static createsInstance(): Promise<void>;
7
7
  protected static runInstallsReleaseForEachRepoPath(): Promise<void>;
8
- protected static runThrowsIfRepoDoesNotHavePreviousRelease(): Promise<void>;
9
8
  protected static commitsChangesToGit(): Promise<void>;
9
+ protected static doesNotInstallIfAlreadyUpToDate(): Promise<void>;
10
+ protected static throwsIfRepoDoesNotHavePreviousRelease(): Promise<void>;
11
+ protected static throwsIfGitHasUncommittedChanges(): Promise<void>;
10
12
  private static run;
11
13
  private static setFakeExec;
12
14
  private static setFakeReadFile;
@@ -6,7 +6,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
6
6
  };
7
7
  import { exec as execSync } from 'node:child_process';
8
8
  import { promisify } from 'node:util';
9
- import { callsToExec, fakeExec, fakeReadFile, resetCallsToExec, resetCallsToReadFile, setFakeReadFileResult, } from '@neurodevs/fake-node-core';
9
+ import { callsToExec, fakeExec, fakeReadFile, resetCallsToExec, resetCallsToReadFile, setFakeExecResult, setFakeReadFileResult, } from '@neurodevs/fake-node-core';
10
10
  import { test, assert } from '@neurodevs/node-tdd';
11
11
  import GitAutocommit from '../../impl/GitAutocommit.js';
12
12
  import NpmReleasePropagator from '../../impl/NpmReleasePropagator.js';
@@ -38,16 +38,7 @@ export default class NpmReleasePropagatorTest extends AbstractPackageTest {
38
38
  options: { cwd: this.repoPaths[1] },
39
39
  },
40
40
  ];
41
- assert.isEqualDeep([callsToExec[0], callsToExec[2]], expectedCalls, 'Did not install release in each repo path!');
42
- }
43
- static async runThrowsIfRepoDoesNotHavePreviousRelease() {
44
- const missingPackageName = this.generateId();
45
- this.instance = this.NpmReleasePropagator({
46
- packageName: missingPackageName,
47
- });
48
- await assert.doesThrowAsync(async () => {
49
- await this.run();
50
- }, `Cannot propagate release for ${missingPackageName} because it is not listed in either dependencies or devDependencies! Please install it in the target repository before running propagation.`);
41
+ assert.isEqualDeep([callsToExec[2], callsToExec[4]], expectedCalls, 'Did not install release in each repo path!');
51
42
  }
52
43
  static async commitsChangesToGit() {
53
44
  await this.run();
@@ -62,6 +53,39 @@ export default class NpmReleasePropagatorTest extends AbstractPackageTest {
62
53
  },
63
54
  ], 'Did not commit changes to git for each repo path!');
64
55
  }
56
+ static async doesNotInstallIfAlreadyUpToDate() {
57
+ setFakeReadFileResult(`${this.repoPaths[0]}/package.json`, JSON.stringify(this.generatePackageJson({
58
+ dependencies: {
59
+ [this.packageName]: this.packageVersion,
60
+ },
61
+ })));
62
+ await this.run();
63
+ const execCalls = callsToExec.filter((call) => call.command.includes(`yarn add`));
64
+ assert.isEqual(execCalls.length, 1, 'Should not have installed update!');
65
+ }
66
+ static async throwsIfRepoDoesNotHavePreviousRelease() {
67
+ const missingPackageName = this.generateId();
68
+ this.instance = this.NpmReleasePropagator({
69
+ packageName: missingPackageName,
70
+ });
71
+ await assert.doesThrowAsync(async () => {
72
+ await this.run();
73
+ }, `Cannot propagate release for ${missingPackageName} because it is not listed in either dependencies or devDependencies! Please install it in the target repository before running propagation.`);
74
+ }
75
+ static async throwsIfGitHasUncommittedChanges() {
76
+ setFakeExecResult('git status --porcelain', {
77
+ stdout: 'M somefile.ts',
78
+ });
79
+ await assert.doesThrowAsync(async () => {
80
+ await this.run();
81
+ }, `Cannot propagate release because there are uncommitted git changes in the following repositories:
82
+
83
+ \t - ${this.repoPaths[0]}
84
+ \t - ${this.repoPaths[1]}
85
+
86
+ Please commit or stash these changes before running propagation!
87
+ `);
88
+ }
65
89
  static async run() {
66
90
  await this.instance.run();
67
91
  }
@@ -76,12 +100,12 @@ export default class NpmReleasePropagatorTest extends AbstractPackageTest {
76
100
  resetCallsToReadFile();
77
101
  setFakeReadFileResult(`${this.repoPaths[0]}/package.json`, JSON.stringify(this.generatePackageJson({
78
102
  dependencies: {
79
- [this.packageName]: this.generateId(),
103
+ [this.packageName]: '2.0.0',
80
104
  },
81
105
  })));
82
106
  setFakeReadFileResult(`${this.repoPaths[1]}/package.json`, JSON.stringify(this.generatePackageJson({
83
107
  devDependencies: {
84
- [this.packageName]: this.generateId(),
108
+ [this.packageName]: '2.0.0',
85
109
  },
86
110
  })));
87
111
  }
@@ -106,8 +130,14 @@ __decorate([
106
130
  ], NpmReleasePropagatorTest, "runInstallsReleaseForEachRepoPath", null);
107
131
  __decorate([
108
132
  test()
109
- ], NpmReleasePropagatorTest, "runThrowsIfRepoDoesNotHavePreviousRelease", null);
133
+ ], NpmReleasePropagatorTest, "commitsChangesToGit", null);
110
134
  __decorate([
111
135
  test()
112
- ], NpmReleasePropagatorTest, "commitsChangesToGit", null);
136
+ ], NpmReleasePropagatorTest, "doesNotInstallIfAlreadyUpToDate", null);
137
+ __decorate([
138
+ test()
139
+ ], NpmReleasePropagatorTest, "throwsIfRepoDoesNotHavePreviousRelease", null);
140
+ __decorate([
141
+ test()
142
+ ], NpmReleasePropagatorTest, "throwsIfGitHasUncommittedChanges", null);
113
143
  //# sourceMappingURL=NpmReleasePropagator.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"NpmReleasePropagator.test.js","sourceRoot":"","sources":["../../../src/__tests__/impl/NpmReleasePropagator.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"NpmReleasePropagator.test.js","sourceRoot":"","sources":["../../../src/__tests__/impl/NpmReleasePropagator.test.ts"],"names":[],"mappings":"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"}
@@ -13,11 +13,14 @@ export default class NpmReleasePropagator implements ReleasePropagator {
13
13
  protected constructor(options: ReleasePropagatorOptions);
14
14
  static Create(options: ReleasePropagatorOptions): ReleasePropagator;
15
15
  run(): Promise<void>;
16
- private throwIfPreviousReleaseNotFound;
16
+ private throwIfUncommittedGitChanges;
17
+ private generateUncommittedErrorFrom;
17
18
  private loadCurrentPackageJson;
18
19
  private get currentPackageJsonPath();
19
- private get isDependency();
20
- private get isDevDependency();
20
+ private throwIfPreviousReleaseNotFound;
21
+ private get dependencyRange();
22
+ private get devDependencyRange();
23
+ private get isUpToDate();
21
24
  private installReleaseForCurrentRepo;
22
25
  private gitCommitChanges;
23
26
  private setCurrentMetaNodeVersion;
@@ -2,6 +2,7 @@ import { exec as execSync } from 'node:child_process';
2
2
  import { readFile } from 'node:fs/promises';
3
3
  import path from 'node:path';
4
4
  import { promisify } from 'node:util';
5
+ import { parse, minVersion, gte } from 'semver';
5
6
  import GitAutocommit from './GitAutocommit.js';
6
7
  export default class NpmReleasePropagator {
7
8
  static Class;
@@ -23,20 +24,42 @@ export default class NpmReleasePropagator {
23
24
  return new (this.Class ?? this)(options);
24
25
  }
25
26
  async run() {
27
+ await this.throwIfUncommittedGitChanges();
26
28
  for (const repoPath of this.repoPaths) {
27
- console.log(`Propagating to ${repoPath}...`);
28
29
  this.currentRepoPath = repoPath;
29
30
  await this.loadCurrentPackageJson();
30
- await this.throwIfPreviousReleaseNotFound();
31
+ this.throwIfPreviousReleaseNotFound();
32
+ if (this.isUpToDate) {
33
+ continue;
34
+ }
35
+ console.log(`Propagating to ${repoPath}...`);
31
36
  await this.installReleaseForCurrentRepo();
32
37
  await this.gitCommitChanges();
33
38
  }
34
39
  }
35
- async throwIfPreviousReleaseNotFound() {
36
- if (!(this.isDependency || this.isDevDependency)) {
37
- throw new Error(`Cannot propagate release for ${this.packageName} because it is not listed in either dependencies or devDependencies! Please install it in the target repository before running propagation.`);
40
+ async throwIfUncommittedGitChanges() {
41
+ const repoPathsWithChanges = [];
42
+ for (const repoPath of this.repoPaths) {
43
+ const changes = await this.exec('git status --porcelain', {
44
+ cwd: repoPath,
45
+ });
46
+ if (changes.stdout.trim().length > 0) {
47
+ repoPathsWithChanges.push(repoPath);
48
+ }
49
+ }
50
+ if (repoPathsWithChanges.length > 0) {
51
+ const err = this.generateUncommittedErrorFrom(repoPathsWithChanges);
52
+ throw new Error(err);
38
53
  }
39
54
  }
55
+ generateUncommittedErrorFrom(repoPathsWithChanges) {
56
+ return `Cannot propagate release because there are uncommitted git changes in the following repositor${repoPathsWithChanges.length > 1 ? 'ies' : 'y'}:
57
+
58
+ \t - ${repoPathsWithChanges.join('\n\t - ')}
59
+
60
+ Please commit or stash these changes before running propagation!
61
+ `;
62
+ }
40
63
  async loadCurrentPackageJson() {
41
64
  const raw = await this.readFile(this.currentPackageJsonPath, 'utf-8');
42
65
  this.currentPackageJson = JSON.parse(raw);
@@ -44,14 +67,29 @@ export default class NpmReleasePropagator {
44
67
  get currentPackageJsonPath() {
45
68
  return `${this.currentRepoPath}/package.json`;
46
69
  }
47
- get isDependency() {
48
- return this.currentPackageJson?.dependencies?.[this.packageName];
70
+ throwIfPreviousReleaseNotFound() {
71
+ if (!(this.dependencyRange || this.devDependencyRange)) {
72
+ throw new Error(`Cannot propagate release for ${this.packageName} because it is not listed in either dependencies or devDependencies! Please install it in the target repository before running propagation.`);
73
+ }
74
+ }
75
+ get dependencyRange() {
76
+ return this.currentPackageJson?.dependencies?.[this.packageName] ?? '';
49
77
  }
50
- get isDevDependency() {
51
- return this.currentPackageJson?.devDependencies?.[this.packageName];
78
+ get devDependencyRange() {
79
+ return (this.currentPackageJson?.devDependencies?.[this.packageName] ?? '');
80
+ }
81
+ get isUpToDate() {
82
+ const target = parse(this.packageVersion);
83
+ if (!target) {
84
+ return false;
85
+ }
86
+ const depMin = minVersion(this.dependencyRange ?? '');
87
+ const devDepMin = minVersion(this.devDependencyRange ?? '');
88
+ return ((depMin && gte(depMin, target)) ||
89
+ (devDepMin && gte(devDepMin, target)));
52
90
  }
53
91
  async installReleaseForCurrentRepo() {
54
- await this.exec(`yarn add ${this.isDevDependency ? '-D ' : ''}${this.packageName}@${this.packageVersion}`, {
92
+ await this.exec(`yarn add ${this.devDependencyRange ? '-D ' : ''}${this.packageName}@${this.packageVersion}`, {
55
93
  cwd: this.currentRepoPath,
56
94
  });
57
95
  }
@@ -1 +1 @@
1
- {"version":3,"file":"NpmReleasePropagator.js","sourceRoot":"","sources":["../../src/impl/NpmReleasePropagator.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"NpmReleasePropagator.js","sourceRoot":"","sources":["../../src/impl/NpmReleasePropagator.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neurodevs/meta-node",
3
- "version": "0.17.0",
3
+ "version": "0.17.2",
4
4
  "description": "Meta-layer utilities for maintaining Node.js package ecosystems.",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -56,7 +56,7 @@
56
56
  "devDependencies": {
57
57
  "@neurodevs/fake-node-core": "^0.7.3",
58
58
  "@neurodevs/generate-id": "^1.0.9",
59
- "@neurodevs/node-tdd": "^0.2.3",
59
+ "@neurodevs/node-tdd": "0.2.3",
60
60
  "@types/fs-extra": "^11.0.4",
61
61
  "@types/node": "^24.6.2",
62
62
  "@types/node-fetch": "^2.6.13",
@@ -1,5 +1,6 @@
1
- import { exec as execSync } from 'node:child_process'
1
+ import { ChildProcess, exec as execSync } from 'node:child_process'
2
2
  import { readFile } from 'node:fs/promises'
3
+ import { Readable } from 'node:stream'
3
4
  import { promisify } from 'node:util'
4
5
  import {
5
6
  callsToExec,
@@ -7,6 +8,7 @@ import {
7
8
  fakeReadFile,
8
9
  resetCallsToExec,
9
10
  resetCallsToReadFile,
11
+ setFakeExecResult,
10
12
  setFakeReadFileResult,
11
13
  } from '@neurodevs/fake-node-core'
12
14
  import { test, assert } from '@neurodevs/node-tdd'
@@ -57,25 +59,12 @@ export default class NpmReleasePropagatorTest extends AbstractPackageTest {
57
59
  ]
58
60
 
59
61
  assert.isEqualDeep(
60
- [callsToExec[0], callsToExec[2]],
62
+ [callsToExec[2], callsToExec[4]],
61
63
  expectedCalls,
62
64
  'Did not install release in each repo path!'
63
65
  )
64
66
  }
65
67
 
66
- @test()
67
- protected static async runThrowsIfRepoDoesNotHavePreviousRelease() {
68
- const missingPackageName = this.generateId()
69
-
70
- this.instance = this.NpmReleasePropagator({
71
- packageName: missingPackageName,
72
- })
73
-
74
- await assert.doesThrowAsync(async () => {
75
- await this.run()
76
- }, `Cannot propagate release for ${missingPackageName} because it is not listed in either dependencies or devDependencies! Please install it in the target repository before running propagation.`)
77
- }
78
-
79
68
  @test()
80
69
  protected static async commitsChangesToGit() {
81
70
  await this.run()
@@ -96,6 +85,61 @@ export default class NpmReleasePropagatorTest extends AbstractPackageTest {
96
85
  )
97
86
  }
98
87
 
88
+ @test()
89
+ protected static async doesNotInstallIfAlreadyUpToDate() {
90
+ setFakeReadFileResult(
91
+ `${this.repoPaths[0]}/package.json`,
92
+ JSON.stringify(
93
+ this.generatePackageJson({
94
+ dependencies: {
95
+ [this.packageName]: this.packageVersion,
96
+ },
97
+ })
98
+ )
99
+ )
100
+
101
+ await this.run()
102
+
103
+ const execCalls = callsToExec.filter((call) =>
104
+ call.command.includes(`yarn add`)
105
+ )
106
+
107
+ assert.isEqual(execCalls.length, 1, 'Should not have installed update!')
108
+ }
109
+
110
+ @test()
111
+ protected static async throwsIfRepoDoesNotHavePreviousRelease() {
112
+ const missingPackageName = this.generateId()
113
+
114
+ this.instance = this.NpmReleasePropagator({
115
+ packageName: missingPackageName,
116
+ })
117
+
118
+ await assert.doesThrowAsync(async () => {
119
+ await this.run()
120
+ }, `Cannot propagate release for ${missingPackageName} because it is not listed in either dependencies or devDependencies! Please install it in the target repository before running propagation.`)
121
+ }
122
+
123
+ @test()
124
+ protected static async throwsIfGitHasUncommittedChanges() {
125
+ setFakeExecResult('git status --porcelain', {
126
+ stdout: 'M somefile.ts' as unknown as Readable,
127
+ } as ChildProcess)
128
+
129
+ await assert.doesThrowAsync(
130
+ async () => {
131
+ await this.run()
132
+ },
133
+ `Cannot propagate release because there are uncommitted git changes in the following repositories:
134
+
135
+ \t - ${this.repoPaths[0]}
136
+ \t - ${this.repoPaths[1]}
137
+
138
+ Please commit or stash these changes before running propagation!
139
+ `
140
+ )
141
+ }
142
+
99
143
  private static async run() {
100
144
  await this.instance.run()
101
145
  }
@@ -117,7 +161,7 @@ export default class NpmReleasePropagatorTest extends AbstractPackageTest {
117
161
  JSON.stringify(
118
162
  this.generatePackageJson({
119
163
  dependencies: {
120
- [this.packageName]: this.generateId(),
164
+ [this.packageName]: '2.0.0',
121
165
  },
122
166
  })
123
167
  )
@@ -128,7 +172,7 @@ export default class NpmReleasePropagatorTest extends AbstractPackageTest {
128
172
  JSON.stringify(
129
173
  this.generatePackageJson({
130
174
  devDependencies: {
131
- [this.packageName]: this.generateId(),
175
+ [this.packageName]: '2.0.0',
132
176
  },
133
177
  })
134
178
  )
@@ -3,6 +3,8 @@ import { readFile } from 'node:fs/promises'
3
3
  import path from 'node:path'
4
4
  import { promisify } from 'node:util'
5
5
 
6
+ import { parse, minVersion, gte } from 'semver'
7
+
6
8
  import GitAutocommit from './GitAutocommit.js'
7
9
 
8
10
  export default class NpmReleasePropagator implements ReleasePropagator {
@@ -31,25 +33,53 @@ export default class NpmReleasePropagator implements ReleasePropagator {
31
33
  }
32
34
 
33
35
  public async run() {
36
+ await this.throwIfUncommittedGitChanges()
37
+
34
38
  for (const repoPath of this.repoPaths) {
35
- console.log(`Propagating to ${repoPath}...`)
36
39
  this.currentRepoPath = repoPath
37
40
 
38
41
  await this.loadCurrentPackageJson()
39
- await this.throwIfPreviousReleaseNotFound()
42
+
43
+ this.throwIfPreviousReleaseNotFound()
44
+
45
+ if (this.isUpToDate) {
46
+ continue
47
+ }
48
+
49
+ console.log(`Propagating to ${repoPath}...`)
40
50
  await this.installReleaseForCurrentRepo()
41
51
  await this.gitCommitChanges()
42
52
  }
43
53
  }
44
54
 
45
- private async throwIfPreviousReleaseNotFound() {
46
- if (!(this.isDependency || this.isDevDependency)) {
47
- throw new Error(
48
- `Cannot propagate release for ${this.packageName} because it is not listed in either dependencies or devDependencies! Please install it in the target repository before running propagation.`
49
- )
55
+ private async throwIfUncommittedGitChanges() {
56
+ const repoPathsWithChanges: string[] = []
57
+
58
+ for (const repoPath of this.repoPaths) {
59
+ const changes = await this.exec('git status --porcelain', {
60
+ cwd: repoPath,
61
+ })
62
+
63
+ if (changes.stdout.trim().length > 0) {
64
+ repoPathsWithChanges.push(repoPath)
65
+ }
66
+ }
67
+
68
+ if (repoPathsWithChanges.length > 0) {
69
+ const err = this.generateUncommittedErrorFrom(repoPathsWithChanges)
70
+ throw new Error(err)
50
71
  }
51
72
  }
52
73
 
74
+ private generateUncommittedErrorFrom(repoPathsWithChanges: string[]) {
75
+ return `Cannot propagate release because there are uncommitted git changes in the following repositor${repoPathsWithChanges.length > 1 ? 'ies' : 'y'}:
76
+
77
+ \t - ${repoPathsWithChanges.join('\n\t - ')}
78
+
79
+ Please commit or stash these changes before running propagation!
80
+ `
81
+ }
82
+
53
83
  private async loadCurrentPackageJson() {
54
84
  const raw = await this.readFile(this.currentPackageJsonPath, 'utf-8')
55
85
  this.currentPackageJson = JSON.parse(raw)
@@ -59,17 +89,43 @@ export default class NpmReleasePropagator implements ReleasePropagator {
59
89
  return `${this.currentRepoPath}/package.json`
60
90
  }
61
91
 
62
- private get isDependency() {
63
- return this.currentPackageJson?.dependencies?.[this.packageName]
92
+ private throwIfPreviousReleaseNotFound() {
93
+ if (!(this.dependencyRange || this.devDependencyRange)) {
94
+ throw new Error(
95
+ `Cannot propagate release for ${this.packageName} because it is not listed in either dependencies or devDependencies! Please install it in the target repository before running propagation.`
96
+ )
97
+ }
98
+ }
99
+
100
+ private get dependencyRange() {
101
+ return this.currentPackageJson?.dependencies?.[this.packageName] ?? ''
64
102
  }
65
103
 
66
- private get isDevDependency() {
67
- return this.currentPackageJson?.devDependencies?.[this.packageName]
104
+ private get devDependencyRange() {
105
+ return (
106
+ this.currentPackageJson?.devDependencies?.[this.packageName] ?? ''
107
+ )
108
+ }
109
+
110
+ private get isUpToDate() {
111
+ const target = parse(this.packageVersion)
112
+
113
+ if (!target) {
114
+ return false
115
+ }
116
+
117
+ const depMin = minVersion(this.dependencyRange ?? '')
118
+ const devDepMin = minVersion(this.devDependencyRange ?? '')
119
+
120
+ return (
121
+ (depMin && gte(depMin, target)) ||
122
+ (devDepMin && gte(devDepMin, target))
123
+ )
68
124
  }
69
125
 
70
126
  private async installReleaseForCurrentRepo() {
71
127
  await this.exec(
72
- `yarn add ${this.isDevDependency ? '-D ' : ''}${this.packageName}@${this.packageVersion}`,
128
+ `yarn add ${this.devDependencyRange ? '-D ' : ''}${this.packageName}@${this.packageVersion}`,
73
129
  {
74
130
  cwd: this.currentRepoPath,
75
131
  }