@toptal/davinci-monorepo 6.7.2-alpha-fix-package-version-test.0 → 6.7.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # @toptal/davinci-monorepo
2
2
 
3
+ ## 6.7.2
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [[`b12d1d19`](https://github.com/toptal/davinci/commit/b12d1d19af02104b9076a53b6e516198e6cda141)]:
8
+ - @toptal/davinci-cli-shared@1.8.1
9
+
3
10
  ## 6.7.1
4
11
 
5
12
  ### Patch Changes
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@toptal/davinci-monorepo",
3
+ "version": "6.7.2",
4
+ "description": "Monorepo utility tools",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "keywords": [
9
+ "lint"
10
+ ],
11
+ "author": "Toptal",
12
+ "homepage": "https://github.com/toptal/davinci/tree/master/packages/monorepo#readme",
13
+ "license": "ISC",
14
+ "main": "src/index.js",
15
+ "bin": {
16
+ "davinci-monorepo": "bin/davinci-monorepo.js"
17
+ },
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/toptal/davinci.git"
21
+ },
22
+ "scripts": {
23
+ "build:package": "../../bin/build-package.js",
24
+ "prepublishOnly": "../../bin/prepublish.js",
25
+ "test": "echo \"Error: run tests from root\" && exit 1"
26
+ },
27
+ "bugs": {
28
+ "url": "https://github.com/toptal/davinci/issues"
29
+ },
30
+ "dependencies": {
31
+ "@toptal/davinci-cli-shared": "1.8.1",
32
+ "glob": "^8.0.3",
33
+ "execa": "^5.1.1",
34
+ "chalk": "^4.1.2",
35
+ "@oclif/core": "^1.16.1",
36
+ "rambda": "^7.2.1",
37
+ "dependency-cruiser": "^11.15.0",
38
+ "ora": "^5.4.1"
39
+ }
40
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toptal/davinci-monorepo",
3
- "version": "6.7.2-alpha-fix-package-version-test.0+e47569ec",
3
+ "version": "6.7.2",
4
4
  "description": "Monorepo utility tools",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -28,14 +28,13 @@
28
28
  "url": "https://github.com/toptal/davinci/issues"
29
29
  },
30
30
  "dependencies": {
31
- "@oclif/core": "^1.16.1",
32
- "@toptal/davinci-cli-shared": "1.8.1-alpha-fix-package-version-test.29+e47569ec",
31
+ "@toptal/davinci-cli-shared": "1.8.1",
32
+ "glob": "^8.0.3",
33
+ "execa": "^5.1.1",
33
34
  "chalk": "^4.1.2",
35
+ "@oclif/core": "^1.16.1",
36
+ "rambda": "^7.2.1",
34
37
  "dependency-cruiser": "^11.15.0",
35
- "execa": "^5.1.1",
36
- "glob": "^8.0.3",
37
- "ora": "^5.4.1",
38
- "rambda": "^7.2.1"
39
- },
40
- "gitHead": "e47569ec6bdd4504eb81d13c6bebfa3013b32840"
38
+ "ora": "^5.4.1"
39
+ }
41
40
  }
@@ -0,0 +1,228 @@
1
+ /* eslint-disable max-lines */
2
+ const calculateRelativeResponsibility = jest.fn()
3
+ const calculateRelations = jest.fn()
4
+
5
+ jest.mock('./relative-responsibility', () => ({
6
+ ...jest.requireActual('./relative-responsibility'),
7
+ calculate: calculateRelativeResponsibility,
8
+ }))
9
+
10
+ jest.mock('./package-relations', () => ({
11
+ ...jest.requireActual('./package-relations'),
12
+ calculate: calculateRelations,
13
+ }))
14
+
15
+ const { info, INST, calculate } = require('./package-instability')
16
+ const { RELATIVE_RESPONSIBILITY } = require('./relative-responsibility')
17
+ const { PAR, PER } = require('./package-relations')
18
+
19
+ describe('metrics: package instability', () => {
20
+ beforeEach(() => {
21
+ calculateRelativeResponsibility.mockReturnValue({})
22
+ calculateRelations.mockReturnValue({})
23
+ })
24
+
25
+ it('returns metric info', () => {
26
+ expect(info).toEqual({
27
+ [INST]: {
28
+ name: 'Instability',
29
+ },
30
+ })
31
+ })
32
+
33
+ describe('when Relative Responsibility of package2, package3 equals 1', () => {
34
+ describe('when PAR equals 1, PER equals 2', () => {
35
+ it('returns zero instability', () => {
36
+ const result = calculate[2]({
37
+ packagesInfo: {
38
+ package1: {
39
+ dependencies: ['package2', 'package3'],
40
+ [PAR]: 1,
41
+ [PER]: 2,
42
+ },
43
+ package2: {
44
+ dependencies: [],
45
+ [RELATIVE_RESPONSIBILITY]: 1,
46
+ [PAR]: 1,
47
+ [PER]: 1,
48
+ },
49
+ package3: {
50
+ dependencies: [],
51
+ [RELATIVE_RESPONSIBILITY]: 1,
52
+ [PAR]: 1,
53
+ [PER]: 1,
54
+ },
55
+ },
56
+ })
57
+
58
+ expect(result).toEqual({
59
+ package1: {
60
+ instability: 0,
61
+ },
62
+ package2: {
63
+ instability: 0,
64
+ },
65
+ package3: {
66
+ instability: 0,
67
+ },
68
+ })
69
+ })
70
+ })
71
+ })
72
+
73
+ describe('when Relative Responsibility of package2 equals 1, package2 equals 0.2', () => {
74
+ describe('when PAR equals 1, PER equals 2', () => {
75
+ it('returns non zero instability', () => {
76
+ const result = calculate[2]({
77
+ packagesInfo: {
78
+ package1: {
79
+ dependencies: ['package2', 'package3'],
80
+ [PAR]: 1,
81
+ [PER]: 2,
82
+ },
83
+ package2: {
84
+ dependencies: [],
85
+ [RELATIVE_RESPONSIBILITY]: 1,
86
+ [PAR]: 1,
87
+ [PER]: 1,
88
+ },
89
+ package3: {
90
+ dependencies: [],
91
+ [RELATIVE_RESPONSIBILITY]: 0.2,
92
+ [PAR]: 1,
93
+ [PER]: 1,
94
+ },
95
+ },
96
+ })
97
+
98
+ expect(result).toEqual({
99
+ package1: {
100
+ instability: 0.26667,
101
+ },
102
+ package2: {
103
+ instability: 0,
104
+ },
105
+ package3: {
106
+ instability: 0,
107
+ },
108
+ })
109
+ })
110
+ })
111
+ })
112
+
113
+ describe('when Relative Responsibility of package2, package3 equals 0', () => {
114
+ describe('when PAR equals 0, PER equals 1', () => {
115
+ it('returns instability = 1', () => {
116
+ const result = calculate[2]({
117
+ packagesInfo: {
118
+ package1: {
119
+ dependencies: ['package2'],
120
+ [PAR]: 0,
121
+ [PER]: 1,
122
+ },
123
+ package2: {
124
+ dependencies: [],
125
+ [RELATIVE_RESPONSIBILITY]: 0,
126
+ [PAR]: 1,
127
+ [PER]: 1,
128
+ },
129
+ package3: {
130
+ dependencies: [],
131
+ [RELATIVE_RESPONSIBILITY]: 0,
132
+ [PAR]: 1,
133
+ [PER]: 1,
134
+ },
135
+ },
136
+ })
137
+
138
+ expect(result).toEqual({
139
+ package1: {
140
+ instability: 1,
141
+ },
142
+ package2: {
143
+ instability: 0,
144
+ },
145
+ package3: {
146
+ instability: 0,
147
+ },
148
+ })
149
+ })
150
+ })
151
+
152
+ describe('when PAR equals 1, PER equals 1', () => {
153
+ it('returns non zero instability', () => {
154
+ const result = calculate[2]({
155
+ packagesInfo: {
156
+ package1: {
157
+ dependencies: ['package2'],
158
+ [PAR]: 1,
159
+ [PER]: 1,
160
+ },
161
+ package2: {
162
+ dependencies: [],
163
+ [RELATIVE_RESPONSIBILITY]: 0,
164
+ [PAR]: 1,
165
+ [PER]: 1,
166
+ },
167
+ package3: {
168
+ dependencies: [],
169
+ [RELATIVE_RESPONSIBILITY]: 0,
170
+ [PAR]: 1,
171
+ [PER]: 1,
172
+ },
173
+ },
174
+ })
175
+
176
+ expect(result).toEqual({
177
+ package1: {
178
+ instability: 0.5,
179
+ },
180
+ package2: {
181
+ instability: 0,
182
+ },
183
+ package3: {
184
+ instability: 0,
185
+ },
186
+ })
187
+ })
188
+ })
189
+
190
+ describe('when PAR equals 1, PER equals 2', () => {
191
+ it('returns non zero instability', () => {
192
+ const result = calculate[2]({
193
+ packagesInfo: {
194
+ package1: {
195
+ dependencies: ['package2', 'package3'],
196
+ [PAR]: 1,
197
+ [PER]: 2,
198
+ },
199
+ package2: {
200
+ dependencies: [],
201
+ [RELATIVE_RESPONSIBILITY]: 0,
202
+ [PAR]: 1,
203
+ [PER]: 1,
204
+ },
205
+ package3: {
206
+ dependencies: [],
207
+ [RELATIVE_RESPONSIBILITY]: 0,
208
+ [PAR]: 1,
209
+ [PER]: 1,
210
+ },
211
+ },
212
+ })
213
+
214
+ expect(result).toEqual({
215
+ package1: {
216
+ instability: 0.66667,
217
+ },
218
+ package2: {
219
+ instability: 0,
220
+ },
221
+ package3: {
222
+ instability: 0,
223
+ },
224
+ })
225
+ })
226
+ })
227
+ })
228
+ })
@@ -0,0 +1,84 @@
1
+ const { calculate, info, PAR, PER } = require('./package-relations')
2
+
3
+ const packagesInfo = {
4
+ package1: {
5
+ name: 'package1',
6
+ dependents: ['package2', 'package3'],
7
+ dependencies: [],
8
+ },
9
+ package2: {
10
+ name: 'package2',
11
+ dependents: ['package3'],
12
+ dependencies: ['package1'],
13
+ },
14
+ package3: {
15
+ name: 'package3',
16
+ dependents: [],
17
+ dependencies: ['package1', 'package2'],
18
+ },
19
+ }
20
+
21
+ describe('metrics: package relations', () => {
22
+ it('returns metric info', () => {
23
+ expect(info).toEqual({
24
+ [PAR]: {
25
+ name: 'Package Afferent Relations',
26
+ },
27
+ [PER]: {
28
+ name: 'Package Efferent Relations',
29
+ },
30
+ })
31
+ })
32
+
33
+ describe('when there are 3 dependent packages', () => {
34
+ it('calculates and returns relations metrics', () => {
35
+ const result = calculate({ packagesInfo })
36
+
37
+ expect(result).toEqual({
38
+ package1: {
39
+ [PAR]: 2,
40
+ [PER]: 0,
41
+ },
42
+ package2: {
43
+ [PAR]: 1,
44
+ [PER]: 1,
45
+ },
46
+ package3: {
47
+ [PAR]: 0,
48
+ [PER]: 2,
49
+ },
50
+ })
51
+ })
52
+ })
53
+
54
+ describe('when there is 1 dependent package', () => {
55
+ it('calculates and returns relations metrics', () => {
56
+ const result = calculate({
57
+ packagesInfo: {
58
+ package1: {
59
+ name: 'package1',
60
+ dependents: [],
61
+ dependencies: [],
62
+ },
63
+ },
64
+ })
65
+
66
+ expect(result).toEqual({
67
+ package1: {
68
+ [PAR]: 0,
69
+ [PER]: 0,
70
+ },
71
+ })
72
+ })
73
+ })
74
+
75
+ describe('when there are NO packages', () => {
76
+ it('calculates and returns empty object', () => {
77
+ const result = calculate({
78
+ packagesInfo: {},
79
+ })
80
+
81
+ expect(result).toEqual({})
82
+ })
83
+ })
84
+ })
@@ -0,0 +1,60 @@
1
+ const calculateRelativeResponsibility = jest.fn()
2
+
3
+ jest.mock('./responsibility', () => ({
4
+ ...jest.requireActual('./responsibility'),
5
+ calculate: calculateRelativeResponsibility,
6
+ }))
7
+
8
+ const {
9
+ calculate,
10
+ info,
11
+ RELATIVE_RESPONSIBILITY,
12
+ } = require('./relative-responsibility')
13
+ const { RESPONSIBILITY } = require('./responsibility')
14
+
15
+ describe('metrics: package normalized responsibility', () => {
16
+ beforeEach(() => {
17
+ calculateRelativeResponsibility.mockReturnValue({})
18
+ })
19
+
20
+ it('returns metric info', () => {
21
+ expect(info).toEqual({
22
+ [RELATIVE_RESPONSIBILITY]: {
23
+ name: 'Relative Responsibility',
24
+ },
25
+ })
26
+ })
27
+
28
+ describe('when package1 has the highest "RESPONSIBILITY"', () => {
29
+ it('returns 1 RELATIVE_RESPONSIBILITY metric for the package1', () => {
30
+ const response = calculate[1]({
31
+ packagesInfo: {
32
+ package1: {
33
+ name: 'package1',
34
+ [RESPONSIBILITY]: 3,
35
+ },
36
+ package2: {
37
+ name: 'package2',
38
+ [RESPONSIBILITY]: 2,
39
+ },
40
+ package3: {
41
+ name: 'package3',
42
+ [RESPONSIBILITY]: 1,
43
+ },
44
+ },
45
+ })
46
+
47
+ expect(response).toEqual({
48
+ package1: {
49
+ [RELATIVE_RESPONSIBILITY]: 1,
50
+ },
51
+ package2: {
52
+ [RELATIVE_RESPONSIBILITY]: 0.6666666666666666,
53
+ },
54
+ package3: {
55
+ [RELATIVE_RESPONSIBILITY]: 0.3333333333333333,
56
+ },
57
+ })
58
+ })
59
+ })
60
+ })
@@ -0,0 +1,108 @@
1
+ const { RESPONSIBILITY, calculate, info } = require('./responsibility')
2
+
3
+ describe('metrics: package relative responsibility', () => {
4
+ it('returns metric info', () => {
5
+ expect(info).toEqual({
6
+ [RESPONSIBILITY]: {
7
+ name: 'Responsibility',
8
+ },
9
+ })
10
+ })
11
+
12
+ it('returns a "RESPONSIBILITY" metrics for each package', () => {
13
+ const result = calculate({
14
+ packagesInfo: {
15
+ package1: {
16
+ dependents: ['package2', 'package3'],
17
+ dependencies: [],
18
+ },
19
+ package2: {
20
+ dependents: ['package3'],
21
+ dependencies: ['package1'],
22
+ },
23
+ package3: {
24
+ dependents: [],
25
+ dependencies: ['package1', 'package2'],
26
+ },
27
+ },
28
+ })
29
+
30
+ expect(result).toEqual({
31
+ package1: {
32
+ [RESPONSIBILITY]: 0.75,
33
+ },
34
+ package2: {
35
+ [RESPONSIBILITY]: 0.25,
36
+ },
37
+ package3: {
38
+ [RESPONSIBILITY]: 0,
39
+ },
40
+ })
41
+ })
42
+
43
+ describe('when all packages have dependencies', () => {
44
+ it('returns a "RESPONSIBILITY" metrics for each package', () => {
45
+ const result = calculate({
46
+ packagesInfo: {
47
+ package1: {
48
+ dependents: ['package2', 'package3'],
49
+ dependencies: ['package3'],
50
+ },
51
+ package2: {
52
+ dependents: ['package3'],
53
+ dependencies: ['package1'],
54
+ },
55
+ package3: {
56
+ dependents: [],
57
+ dependencies: ['package1', 'package2'],
58
+ },
59
+ },
60
+ })
61
+
62
+ expect(result).toEqual({
63
+ package1: {
64
+ [RESPONSIBILITY]: 0.5,
65
+ },
66
+ package2: {
67
+ [RESPONSIBILITY]: 0.16666666666666666,
68
+ },
69
+ package3: {
70
+ [RESPONSIBILITY]: 0,
71
+ },
72
+ })
73
+ })
74
+ })
75
+
76
+ describe('when packages are isolated', () => {
77
+ it('returns a "RESPONSIBILITY" metrics for each package', () => {
78
+ const result = calculate({
79
+ packagesInfo: {
80
+ package1: {
81
+ dependents: [],
82
+ dependencies: [],
83
+ },
84
+ package2: {
85
+ dependents: [],
86
+ dependencies: [],
87
+ },
88
+ package3: {
89
+ dependents: [],
90
+ dependencies: [],
91
+ },
92
+ },
93
+ })
94
+
95
+ expect(result).toEqual({
96
+ package1: {
97
+ [RESPONSIBILITY]: NaN,
98
+ },
99
+ package2: {
100
+ [RESPONSIBILITY]: NaN,
101
+ },
102
+ package3: {
103
+ [RESPONSIBILITY]: NaN,
104
+ },
105
+ })
106
+ })
107
+ })
108
+ })
@@ -0,0 +1,29 @@
1
+ const csv = require('./csv')
2
+
3
+ const metricInfo = {
4
+ metric1: { name: 'Metric 1' },
5
+ metric2: { name: 'Metric 2' },
6
+ }
7
+
8
+ const packagesInfo = {
9
+ package1: {
10
+ name: 'Package 1',
11
+ metric1: 0,
12
+ metric2: 2.0,
13
+ },
14
+ package2: {
15
+ name: 'Package 2',
16
+ metric1: 1,
17
+ metric2: 3.0,
18
+ },
19
+ }
20
+
21
+ describe('csv renderer', () => {
22
+ it('returns a csv string', () => {
23
+ const result = csv(metricInfo, packagesInfo)
24
+
25
+ expect(result).toBe(
26
+ 'Package Name,Metric 1,Metric 2\n' + 'package1,0,2\n' + 'package2,1,3'
27
+ )
28
+ })
29
+ })
@@ -0,0 +1,69 @@
1
+ const buildTable = jest.fn()
2
+
3
+ jest.mock('@oclif/core', () => ({
4
+ CliUx: {
5
+ ux: {
6
+ table: buildTable,
7
+ },
8
+ },
9
+ }))
10
+
11
+ const raw = require('./raw')
12
+
13
+ const metricInfo = {
14
+ metric1: { name: 'Metric 1' },
15
+ metric2: { name: 'Metric 2' },
16
+ }
17
+
18
+ const packagesInfo = {
19
+ package1: {
20
+ name: 'Package 1',
21
+ metric1: 0,
22
+ metric2: 2.0,
23
+ },
24
+ package2: {
25
+ name: 'Package 2',
26
+ metric1: 1,
27
+ metric2: 3.0,
28
+ },
29
+ }
30
+
31
+ describe('raw renderer', () => {
32
+ beforeEach(() => {
33
+ buildTable.mockImplementation((value, columns, { printLine }) =>
34
+ printLine(`buildTable(${JSON.stringify(value)})`)
35
+ )
36
+ })
37
+
38
+ afterEach(() => {
39
+ buildTable.mockReset()
40
+ })
41
+
42
+ it('returns a raw string', () => {
43
+ const result = raw(metricInfo, packagesInfo)
44
+
45
+ expect(result).toBe(
46
+ 'buildTable([{"name":"Package 1","metric1":0,"metric2":2},{"name":"Package 2","metric1":1,"metric2":3}])\n'
47
+ )
48
+ })
49
+
50
+ it('calls print.prettifyMatrix with proper params', () => {
51
+ raw(metricInfo, packagesInfo)
52
+
53
+ expect(buildTable).toHaveBeenCalledTimes(1)
54
+ expect(buildTable).toHaveBeenCalledWith(
55
+ [
56
+ { name: 'Package 1', metric1: 0, metric2: 2 },
57
+ { name: 'Package 2', metric1: 1, metric2: 3 },
58
+ ],
59
+ {
60
+ metric1: { header: 'Metric 1' },
61
+ metric2: { header: 'Metric 2' },
62
+ name: { header: 'Package Name' },
63
+ },
64
+ {
65
+ printLine: expect.any(Function),
66
+ }
67
+ )
68
+ })
69
+ })
@@ -0,0 +1,28 @@
1
+ jest.mock('execa', () => ({
2
+ ...jest.requireActual('execa'),
3
+ sync: jest.fn(() => true),
4
+ }))
5
+
6
+ const execa = require('execa')
7
+
8
+ const checkIfMonorepo = require('./check-if-monorepo')
9
+
10
+ execa.sync = jest.fn(() => true)
11
+
12
+ describe('checkIfMonorepo', () => {
13
+ it('returns true if the project is monorepo', () => {
14
+ const isMonorepo = checkIfMonorepo()
15
+
16
+ expect(isMonorepo).toBe(true)
17
+ })
18
+
19
+ it('returns false if the project is not a monorepo', () => {
20
+ execa.sync.mockImplementation(() => {
21
+ throw new Error('Cannot execute lerna')
22
+ })
23
+
24
+ const isMonorepo = checkIfMonorepo()
25
+
26
+ expect(isMonorepo).toBe(false)
27
+ })
28
+ })
@@ -0,0 +1,13 @@
1
+ const getCircularityErrorMessage = require('./get-circularity-error-message')
2
+
3
+ describe('getCircularityErrorMessage', () => {
4
+ it('returns one cycle path', () => {
5
+ expect(
6
+ getCircularityErrorMessage([
7
+ ['lib-1', 'lib-3', 'lib-4', 'lib-5', 'lib-1'],
8
+ ])
9
+ ).toBe(
10
+ 'Circular dependencies found:\n1. lib-1 -> lib-3 -> lib-4 -> lib-5 -> lib-1\n'
11
+ )
12
+ })
13
+ })
@@ -0,0 +1,53 @@
1
+ const getGraphCyclePaths = require('./get-graph-cycle-paths')
2
+
3
+ const NO_CYCLES_GRAPH = {
4
+ 'lib-1': ['typescript', 'lib-3'],
5
+ 'lib-2': ['react'],
6
+ 'lib-3': [],
7
+ }
8
+
9
+ const ONE_CYCLE_GRAPH = {
10
+ 'lib-1': ['typescript', 'lib-3'],
11
+ 'lib-2': ['react'],
12
+ 'lib-3': ['lib-1'],
13
+ }
14
+
15
+ const TWO_CYCLES_GRAPH = {
16
+ 'lib-1': ['typescript', 'lib-3'],
17
+ 'lib-2': ['react'],
18
+ 'lib-3': ['lib-1', 'lib-4'],
19
+ 'lib-4': ['lib-3'],
20
+ }
21
+
22
+ const LONG_CYCLE_GRAPH = {
23
+ 'lib-1': ['typescript', 'lib-3'],
24
+ 'lib-2': ['react'],
25
+ 'lib-3': ['lib-4'],
26
+ 'lib-4': ['lib-5', 'lib-2'],
27
+ 'lib-5': ['lib-1'],
28
+ }
29
+
30
+ describe('getGraphCyclePaths', () => {
31
+ it('returns no cycle paths', () => {
32
+ expect(getGraphCyclePaths(NO_CYCLES_GRAPH)).toEqual([])
33
+ })
34
+
35
+ it('returns one cycle path', () => {
36
+ expect(getGraphCyclePaths(ONE_CYCLE_GRAPH)).toEqual([
37
+ ['lib-1', 'lib-3', 'lib-1'],
38
+ ])
39
+ })
40
+
41
+ it('returns two cycles paths', () => {
42
+ expect(getGraphCyclePaths(TWO_CYCLES_GRAPH)).toEqual([
43
+ ['lib-1', 'lib-3', 'lib-1'],
44
+ ['lib-3', 'lib-4', 'lib-3'],
45
+ ])
46
+ })
47
+
48
+ it('returns long cycle path', () => {
49
+ expect(getGraphCyclePaths(LONG_CYCLE_GRAPH)).toEqual([
50
+ ['lib-1', 'lib-3', 'lib-4', 'lib-5', 'lib-1'],
51
+ ])
52
+ })
53
+ })
@@ -0,0 +1,73 @@
1
+ jest.mock('child_process', () => ({
2
+ execSync: jest.fn(),
3
+ }))
4
+
5
+ const { execSync } = require('child_process')
6
+
7
+ const getLernaGraph = require('./get-lerna-graph')
8
+
9
+ const graph = {
10
+ package1: ['externalDependency1', 'package2'],
11
+ package2: ['externalDependency2', 'externalDependency3'],
12
+ }
13
+
14
+ describe('getLernaGraph', () => {
15
+ afterEach(() => {
16
+ execSync.mockReset()
17
+ })
18
+
19
+ it('executes the command', () => {
20
+ execSync.mockReturnValue(JSON.stringify(graph))
21
+
22
+ getLernaGraph()
23
+
24
+ expect(execSync).toHaveBeenCalledTimes(1)
25
+ expect(execSync).toHaveBeenCalledWith(
26
+ 'yarn --silent --cwd=../.. lerna list --all --toposort --graph --loglevel silent'
27
+ )
28
+ })
29
+
30
+ it('returns a graph', () => {
31
+ execSync.mockReturnValue(JSON.stringify(graph))
32
+
33
+ const result = getLernaGraph()
34
+
35
+ expect(result).toEqual(graph)
36
+ })
37
+
38
+ describe('when onlyLocalDependencies option is true', () => {
39
+ it('returns graph with only internal dependencies', () => {
40
+ execSync.mockReturnValue(JSON.stringify(graph))
41
+
42
+ const result = getLernaGraph({ onlyLocalDependencies: true })
43
+
44
+ expect(result).toEqual({ package1: ['package2'], package2: [] })
45
+ })
46
+ })
47
+
48
+ describe('when there are some excluded packages', () => {
49
+ it('returns graph without excluded packages', () => {
50
+ execSync.mockReturnValue(
51
+ JSON.stringify({
52
+ package1: ['externalDependency1', 'package2'],
53
+ package2: [
54
+ 'externalDependency2',
55
+ 'externalDependency3',
56
+ 'packageToExclude2',
57
+ ],
58
+ packageToExclude1: ['package1'],
59
+ packageToExclude2: [],
60
+ })
61
+ )
62
+
63
+ const result = getLernaGraph({
64
+ excludedPackages: new Set(['packageToExclude1', 'packageToExclude2']),
65
+ })
66
+
67
+ expect(result).toEqual({
68
+ package1: ['externalDependency1', 'package2'],
69
+ package2: ['externalDependency2', 'externalDependency3'],
70
+ })
71
+ })
72
+ })
73
+ })
@@ -0,0 +1,78 @@
1
+ const glob = require('glob')
2
+ const fs = require('fs')
3
+
4
+ const getPackages = require('./get-packages')
5
+
6
+ const PKG_1 = {
7
+ name: 'pkg-1',
8
+ }
9
+
10
+ const PKG_2 = {
11
+ name: 'pkg-2',
12
+ }
13
+
14
+ jest.mock(
15
+ 'test-project/package.json',
16
+ () => ({
17
+ workspaces: ['packages/*'],
18
+ }),
19
+ { virtual: true }
20
+ )
21
+
22
+ jest.mock('test-project/packages/pkg-1/package.json', () => PKG_1, {
23
+ virtual: true,
24
+ })
25
+
26
+ jest.mock('test-project/packages/pkg-2/package.json', () => PKG_2, {
27
+ virtual: true,
28
+ })
29
+
30
+ describe('getPackages', () => {
31
+ const mockWorkspaces = ['packages/pkg-1', 'packages/pkg-2']
32
+ let fsExistsSyncMock
33
+ let globSyncMock
34
+
35
+ beforeEach(() => {
36
+ fsExistsSyncMock = jest.spyOn(fs, 'existsSync')
37
+ globSyncMock = jest.spyOn(glob, 'sync')
38
+ })
39
+
40
+ afterEach(() => {
41
+ fsExistsSyncMock.mockRestore()
42
+ globSyncMock.mockRestore()
43
+ })
44
+
45
+ it('gets packages correctly', () => {
46
+ fsExistsSyncMock.mockImplementation(() => true)
47
+ globSyncMock.mockImplementation(() => mockWorkspaces)
48
+
49
+ const result = [
50
+ {
51
+ ...PKG_1,
52
+ location: mockWorkspaces[0],
53
+ },
54
+ {
55
+ ...PKG_2,
56
+ location: mockWorkspaces[1],
57
+ },
58
+ ]
59
+
60
+ expect(getPackages('./test-project')).toEqual(result)
61
+ })
62
+
63
+ it('ignores packages without package json', () => {
64
+ fsExistsSyncMock.mockImplementation(path =>
65
+ path.includes('pkg-2/package.json')
66
+ )
67
+ globSyncMock.mockImplementation(() => mockWorkspaces)
68
+
69
+ const result = [
70
+ {
71
+ ...PKG_2,
72
+ location: mockWorkspaces[1],
73
+ },
74
+ ]
75
+
76
+ expect(getPackages('./test-project')).toEqual(result)
77
+ })
78
+ })
@@ -0,0 +1,65 @@
1
+ const glob = require('glob')
2
+ const fs = require('fs')
3
+
4
+ const getWorkspaces = require('./get-workspaces')
5
+
6
+ const LIB_1 = {
7
+ name: 'lib-1',
8
+ dependencies: { typescript: '4.2.0' },
9
+ }
10
+
11
+ const LIB_2 = {
12
+ name: 'lib-2',
13
+ dependencies: { react: '17.0.0' },
14
+ }
15
+
16
+ jest.mock(
17
+ 'test-project/package.json',
18
+ () => ({
19
+ workspaces: ['libs/*'],
20
+ }),
21
+ { virtual: true }
22
+ )
23
+
24
+ jest.mock('test-project/libs/lib-1/package.json', () => LIB_1, {
25
+ virtual: true,
26
+ })
27
+
28
+ jest.mock('test-project/libs/lib-2/package.json', () => LIB_2, {
29
+ virtual: true,
30
+ })
31
+
32
+ describe('getWorkspaces', () => {
33
+ let fsExistsSyncMock
34
+ let globSyncMock
35
+
36
+ beforeEach(() => {
37
+ fsExistsSyncMock = jest.spyOn(fs, 'existsSync')
38
+ globSyncMock = jest.spyOn(glob, 'sync')
39
+ })
40
+
41
+ afterEach(() => {
42
+ fsExistsSyncMock.mockRestore()
43
+ globSyncMock.mockRestore()
44
+ })
45
+
46
+ it('gets workspaces correctly', () => {
47
+ fsExistsSyncMock.mockImplementation(() => true)
48
+ globSyncMock.mockImplementation(() => ['libs/lib-1', 'libs/lib-2'])
49
+
50
+ expect(getWorkspaces('./test-project')).toEqual([LIB_1, LIB_2])
51
+ })
52
+
53
+ it('ignores workspaces without package json', () => {
54
+ fsExistsSyncMock.mockImplementation(path => {
55
+ if (path.includes('lib-1/package.json')) {
56
+ return false
57
+ }
58
+
59
+ return true
60
+ })
61
+ globSyncMock.mockImplementation(() => ['libs/lib-1', 'libs/lib-2'])
62
+
63
+ expect(getWorkspaces('./test-project')).toEqual([LIB_2])
64
+ })
65
+ })
@@ -0,0 +1,48 @@
1
+ const makeWorkspacesGraph = require('./make-workspaces-graph')
2
+
3
+ const workspaces = [
4
+ {
5
+ name: 'lib-1',
6
+ dependencies: { typescript: '4.2.0', 'lib-3': '1.0.0' },
7
+ },
8
+ {
9
+ name: 'lib-2',
10
+ dependencies: { react: '17.0.0' },
11
+ },
12
+ {
13
+ name: 'lib-3',
14
+ },
15
+ ]
16
+
17
+ describe('makeWorkspacesGraph', () => {
18
+ it('makes a graph from workspaces', () => {
19
+ expect(makeWorkspacesGraph(workspaces)).toEqual({
20
+ 'lib-1': ['typescript', 'lib-3'],
21
+ 'lib-2': ['react'],
22
+ 'lib-3': [],
23
+ })
24
+ })
25
+
26
+ it('includes dev dependencies in a generated graph from workspaces', () => {
27
+ const workspacesWithDevDeps = [
28
+ {
29
+ name: 'lib-1',
30
+ dependencies: { react: '17.0.0' },
31
+ devDependencies: { 'react-dom': '18.0.0' },
32
+ },
33
+ {
34
+ name: 'lib-2',
35
+ devDependencies: { lodash: '4.0.1' },
36
+ },
37
+ {
38
+ name: 'lib-3',
39
+ },
40
+ ]
41
+
42
+ expect(makeWorkspacesGraph(workspacesWithDevDeps)).toEqual({
43
+ 'lib-1': ['react', 'react-dom'],
44
+ 'lib-2': ['lodash'],
45
+ 'lib-3': [],
46
+ })
47
+ })
48
+ })