@servicetitan/startup 24.1.0 → 24.1.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/dist/cli/commands/mfe-publish.d.ts.map +1 -1
- package/dist/cli/commands/mfe-publish.js +5 -1
- package/dist/cli/commands/mfe-publish.js.map +1 -1
- package/dist/utils/find-packages.d.ts +6 -0
- package/dist/utils/find-packages.d.ts.map +1 -0
- package/dist/utils/find-packages.js +52 -0
- package/dist/utils/find-packages.js.map +1 -0
- package/dist/utils/get-configuration.d.ts +5 -4
- package/dist/utils/get-configuration.d.ts.map +1 -1
- package/dist/utils/get-configuration.js +15 -9
- package/dist/utils/get-configuration.js.map +1 -1
- package/dist/utils/get-packages.d.ts +1 -5
- package/dist/utils/get-packages.d.ts.map +1 -1
- package/dist/utils/get-packages.js +41 -38
- package/dist/utils/get-packages.js.map +1 -1
- package/package.json +13 -8
- package/src/cli/commands/__tests__/mfe-package-publish.test.ts +8 -0
- package/src/cli/commands/mfe-publish.ts +8 -1
- package/src/utils/__tests__/get-configuration.test.ts +30 -8
- package/src/utils/__tests__/get-packages.test.ts +162 -121
- package/src/utils/find-packages.ts +61 -0
- package/src/utils/get-configuration.ts +18 -8
- package/src/utils/get-packages.ts +55 -53
- package/template/lerna.json +1 -2
- package/template/packages/application/package.json +6 -6
- package/template-react18/packages/application/package.json +6 -6
- package/dist/utils/maybe-create-git-folder.d.ts +0 -10
- package/dist/utils/maybe-create-git-folder.d.ts.map +0 -1
- package/dist/utils/maybe-create-git-folder.js +0 -25
- package/dist/utils/maybe-create-git-folder.js.map +0 -1
- package/src/utils/__tests__/maybe-create-git-folder.test.ts +0 -41
- package/src/utils/maybe-create-git-folder.ts +0 -18
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import { log } from '../log';
|
|
4
|
-
import { maybeCreateGitFolder } from '../maybe-create-git-folder';
|
|
5
|
-
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { fs, vol } from 'memfs';
|
|
6
3
|
import {
|
|
7
4
|
Package,
|
|
8
5
|
PackageType,
|
|
@@ -11,178 +8,222 @@ import {
|
|
|
11
8
|
splitPackagesByType,
|
|
12
9
|
} from '../get-packages';
|
|
13
10
|
|
|
14
|
-
jest.mock('
|
|
15
|
-
jest.mock('../get-configuration');
|
|
16
|
-
jest.mock('../maybe-create-git-folder');
|
|
11
|
+
jest.mock('fs', () => fs);
|
|
17
12
|
|
|
18
13
|
describe('[startup] Utils', () => {
|
|
19
|
-
const packages: Pick<Package, 'name' | 'location' | 'type'>[] = [
|
|
20
|
-
{ name: 'foo', location: 'packages/foo', type: PackageType.Legacy },
|
|
21
|
-
{ name: 'bar', location: 'packages/bar', type: PackageType.TSC },
|
|
22
|
-
{ name: 'baz', location: 'packages/baz', type: PackageType.Webpack },
|
|
23
|
-
];
|
|
24
|
-
|
|
25
14
|
beforeEach(() => jest.clearAllMocks());
|
|
26
15
|
|
|
27
|
-
function expectExecaToBeCalledWith(command: string, options?: execa.SyncOptions) {
|
|
28
|
-
const [file, ...args] = command.split(' ');
|
|
29
|
-
const { calls } = jest.mocked(execa.sync).mock;
|
|
30
|
-
const expected: any[] = [file, args, ...(options ? [options] : [])];
|
|
31
|
-
expect(calls).toContainEqual(expect.arrayContaining(expected));
|
|
32
|
-
}
|
|
33
|
-
|
|
34
16
|
describe(`${getPackages.name}`, () => {
|
|
35
17
|
let options: Parameters<typeof getPackages>[0] | undefined;
|
|
36
|
-
let dependencies: Record<string, string[]> | undefined;
|
|
37
|
-
|
|
38
|
-
function findPackageByLocation(location?: string) {
|
|
39
|
-
return packages.find(p => p.location === location);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function packageGraph() {
|
|
43
|
-
return dependencies ?? Object.fromEntries(packages.map(({ name }) => [name, []]));
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function scopedPackages() {
|
|
47
|
-
let result = packages;
|
|
48
|
-
if (options?.scope) {
|
|
49
|
-
const arrayScope = Array.isArray(options.scope) ? options.scope : [options.scope];
|
|
50
|
-
result = result.filter(({ name }) => arrayScope.includes(name));
|
|
51
|
-
}
|
|
52
|
-
return result;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function resolvePackages(args: string[]) {
|
|
56
|
-
if (args.includes('--graph')) {
|
|
57
|
-
return packageGraph();
|
|
58
|
-
}
|
|
59
|
-
if (args.includes('--scope')) {
|
|
60
|
-
return scopedPackages().map(({ type, ...pkg }) => pkg);
|
|
61
|
-
}
|
|
62
|
-
return packages.map(({ type, ...pkg }) => pkg);
|
|
63
|
-
}
|
|
64
18
|
|
|
65
19
|
beforeEach(() => {
|
|
66
20
|
options = undefined;
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
21
|
+
vol.fromNestedJSON({
|
|
22
|
+
'package.json': JSON.stringify({
|
|
23
|
+
workspaces: ['packages/*', 'examples/*', 'legacy/bar'],
|
|
24
|
+
}),
|
|
25
|
+
'packages': {
|
|
26
|
+
foo: {
|
|
27
|
+
'package.json': JSON.stringify({
|
|
28
|
+
name: 'foo',
|
|
29
|
+
version: '1.0.0',
|
|
30
|
+
private: true,
|
|
31
|
+
}),
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
'legacy': {
|
|
35
|
+
bar: {
|
|
36
|
+
'package.json': JSON.stringify({
|
|
37
|
+
name: 'bar',
|
|
38
|
+
version: '2.0.0',
|
|
39
|
+
cli: { legacy: true },
|
|
40
|
+
}),
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
'examples': {
|
|
44
|
+
baz: {
|
|
45
|
+
'package.json': JSON.stringify({
|
|
46
|
+
name: 'baz',
|
|
47
|
+
version: '3.0.0',
|
|
48
|
+
cli: { webpack: false },
|
|
49
|
+
}),
|
|
50
|
+
},
|
|
51
|
+
},
|
|
78
52
|
});
|
|
79
53
|
});
|
|
80
54
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
55
|
+
afterEach(() => vol.reset());
|
|
56
|
+
|
|
57
|
+
const subject = () =>
|
|
58
|
+
getPackages(options).sort((a, b) => a.version.localeCompare(b.version));
|
|
59
|
+
|
|
60
|
+
test('returns workspace packages', () => {
|
|
61
|
+
expect(subject()).toEqual([
|
|
62
|
+
{
|
|
63
|
+
name: 'foo',
|
|
64
|
+
version: '1.0.0',
|
|
65
|
+
private: true,
|
|
66
|
+
location: path.resolve('./packages/foo'),
|
|
67
|
+
type: PackageType.Webpack,
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
name: 'bar',
|
|
71
|
+
version: '2.0.0',
|
|
72
|
+
private: false,
|
|
73
|
+
location: path.resolve('./legacy/bar'),
|
|
74
|
+
type: PackageType.Legacy,
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
name: 'baz',
|
|
78
|
+
version: '3.0.0',
|
|
79
|
+
private: false,
|
|
80
|
+
location: path.resolve('./examples/baz'),
|
|
81
|
+
type: PackageType.TSC,
|
|
82
|
+
},
|
|
83
|
+
]);
|
|
86
84
|
});
|
|
87
85
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
expect(maybeCreateGitFolder).toHaveBeenCalled();
|
|
92
|
-
});
|
|
86
|
+
describe('with "scope"', () => {
|
|
87
|
+
beforeEach(() => (options = { scope: 'foo' }));
|
|
93
88
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
beforeEach(() => {
|
|
97
|
-
Object.defineProperty(process, 'platform', { value: 'win32' });
|
|
89
|
+
test('returns only the specified packages', () => {
|
|
90
|
+
expect(subject()).toEqual([expect.objectContaining({ name: 'foo' })]);
|
|
98
91
|
});
|
|
99
92
|
|
|
100
|
-
|
|
101
|
-
|
|
93
|
+
describe('when filtered package has dependencies', () => {
|
|
94
|
+
beforeEach(() => {
|
|
95
|
+
const fooPath = path.resolve('./packages/foo/package.json');
|
|
96
|
+
const fooJson = JSON.parse(fs.readFileSync(fooPath, 'utf8').toString());
|
|
97
|
+
fs.writeFileSync(
|
|
98
|
+
fooPath,
|
|
99
|
+
JSON.stringify({ ...fooJson, dependencies: { bar: '>=2.0' } })
|
|
100
|
+
);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test('also returns internal dependencies', () => {
|
|
104
|
+
expect(subject()).toEqual([
|
|
105
|
+
expect.objectContaining({ name: 'foo' }),
|
|
106
|
+
expect.objectContaining({ name: 'bar' }),
|
|
107
|
+
]);
|
|
108
|
+
});
|
|
102
109
|
});
|
|
103
110
|
|
|
104
|
-
|
|
105
|
-
|
|
111
|
+
describe('when "scope" is a glob expression', () => {
|
|
112
|
+
beforeEach(() => (options = { scope: 'ba*' }));
|
|
106
113
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
114
|
+
test('returns only the specified packages', () => {
|
|
115
|
+
expect(subject()).toEqual([
|
|
116
|
+
expect.objectContaining({ name: 'bar' }),
|
|
117
|
+
expect.objectContaining({ name: 'baz' }),
|
|
118
|
+
]);
|
|
111
119
|
});
|
|
112
120
|
});
|
|
113
121
|
});
|
|
114
122
|
|
|
115
|
-
describe
|
|
116
|
-
beforeEach(() => (options = {
|
|
123
|
+
describe('with "ignore"', () => {
|
|
124
|
+
beforeEach(() => (options = { ignore: ['foo'] }));
|
|
125
|
+
|
|
126
|
+
test('omits the specified packages', () => {
|
|
127
|
+
expect(subject()).not.toContainEqual(expect.objectContaining({ name: 'foo' }));
|
|
128
|
+
});
|
|
117
129
|
|
|
118
|
-
|
|
119
|
-
|
|
130
|
+
describe('when "ignore" is a glob expression', () => {
|
|
131
|
+
beforeEach(() => (options = { ignore: ['ba*'] }));
|
|
120
132
|
|
|
121
|
-
|
|
133
|
+
test('returns only the specified packages', () => {
|
|
134
|
+
const result = subject();
|
|
135
|
+
expect(result).not.toContainEqual(expect.objectContaining({ name: 'bar' }));
|
|
136
|
+
expect(result).not.toContainEqual(expect.objectContaining({ name: 'baz' }));
|
|
137
|
+
});
|
|
122
138
|
});
|
|
123
139
|
});
|
|
124
140
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
test('returns filtered packages', () => {
|
|
129
|
-
expect(subject()).toEqual(packages.filter(({ name }) => name === 'foo'));
|
|
141
|
+
function itReturnsEmptyArray() {
|
|
142
|
+
test('returns empty array', () => {
|
|
143
|
+
expect(subject()).toEqual([]);
|
|
130
144
|
});
|
|
145
|
+
}
|
|
131
146
|
|
|
132
|
-
|
|
133
|
-
|
|
147
|
+
describe('with no packages', () => {
|
|
148
|
+
beforeEach(() => vol.fromJSON({ 'package.json': JSON.stringify({}) }));
|
|
134
149
|
|
|
135
|
-
|
|
136
|
-
expect(subject()).toEqual(
|
|
137
|
-
packages.filter(({ name }) => ['foo', 'bar'].includes(name))
|
|
138
|
-
);
|
|
139
|
-
});
|
|
140
|
-
});
|
|
150
|
+
itReturnsEmptyArray();
|
|
141
151
|
});
|
|
142
152
|
|
|
143
|
-
describe('
|
|
144
|
-
beforeEach(() =>
|
|
145
|
-
jest.mocked(execa.sync).mockReturnValue({ stdout: Buffer.from('foo') } as any)
|
|
146
|
-
);
|
|
153
|
+
describe('with no package.json', () => {
|
|
154
|
+
beforeEach(() => vol.reset());
|
|
147
155
|
|
|
148
|
-
|
|
149
|
-
const logSpy = jest.spyOn(log, 'error');
|
|
150
|
-
expect(() => subject()).toThrow();
|
|
151
|
-
expect(logSpy).toHaveBeenCalledWith(expect.stringContaining('\nfoo'));
|
|
152
|
-
});
|
|
156
|
+
itReturnsEmptyArray();
|
|
153
157
|
});
|
|
154
158
|
});
|
|
155
159
|
|
|
156
160
|
describe(`${getPackagesGraph.name}`, () => {
|
|
157
|
-
const packageGraph = Object.fromEntries(packages.map(({ name }) => [name, []]));
|
|
158
161
|
let options: Parameters<typeof getPackagesGraph>[0] | undefined;
|
|
159
162
|
|
|
160
163
|
beforeEach(() => {
|
|
161
164
|
options = undefined;
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
+
vol.fromNestedJSON({
|
|
166
|
+
'package.json': JSON.stringify({ workspaces: ['packages/*'] }),
|
|
167
|
+
'packages': {
|
|
168
|
+
foo: {
|
|
169
|
+
'package.json': JSON.stringify({
|
|
170
|
+
name: 'foo',
|
|
171
|
+
dependencies: { bar: '^1.0.0', react: '>=18' },
|
|
172
|
+
devDependencies: {
|
|
173
|
+
'@testing-library/react': '>=16',
|
|
174
|
+
'@testing-library/dom': '>=10',
|
|
175
|
+
},
|
|
176
|
+
}),
|
|
177
|
+
},
|
|
178
|
+
bar: {
|
|
179
|
+
'package.json': JSON.stringify({
|
|
180
|
+
name: 'bar',
|
|
181
|
+
dependencies: { foo: '*' },
|
|
182
|
+
}),
|
|
183
|
+
},
|
|
184
|
+
baz: {
|
|
185
|
+
'package.json': JSON.stringify({
|
|
186
|
+
name: 'baz',
|
|
187
|
+
}),
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
});
|
|
165
191
|
});
|
|
166
192
|
|
|
167
193
|
const subject = () => getPackagesGraph(options);
|
|
168
194
|
|
|
169
|
-
test('returns
|
|
170
|
-
expect(subject()).toEqual(
|
|
171
|
-
|
|
195
|
+
test('returns package graph', () => {
|
|
196
|
+
expect(subject()).toEqual({
|
|
197
|
+
foo: ['bar', 'react', '@testing-library/react', '@testing-library/dom'].sort(),
|
|
198
|
+
bar: ['foo'],
|
|
199
|
+
baz: [],
|
|
200
|
+
});
|
|
172
201
|
});
|
|
173
202
|
|
|
174
|
-
describe
|
|
175
|
-
beforeEach(() => (options = {
|
|
203
|
+
describe('with "scope"', () => {
|
|
204
|
+
beforeEach(() => (options = { scope: 'foo' }));
|
|
205
|
+
|
|
206
|
+
test('includes only the specified packages', () => {
|
|
207
|
+
expect(Object.keys(subject())).toEqual(['foo']);
|
|
208
|
+
});
|
|
209
|
+
});
|
|
176
210
|
|
|
177
|
-
|
|
178
|
-
|
|
211
|
+
describe('with "ignore"', () => {
|
|
212
|
+
beforeEach(() => (options = { ignore: ['foo', 'bar'] }));
|
|
179
213
|
|
|
180
|
-
|
|
214
|
+
test('omits the specified packages', () => {
|
|
215
|
+
expect(Object.keys(subject())).toEqual(['baz']);
|
|
181
216
|
});
|
|
182
217
|
});
|
|
183
218
|
});
|
|
184
219
|
|
|
185
220
|
describe(`${splitPackagesByType.name}`, () => {
|
|
221
|
+
const packages: Pick<Package, 'name' | 'location' | 'type'>[] = [
|
|
222
|
+
{ name: 'foo', location: 'packages/foo', type: PackageType.Legacy },
|
|
223
|
+
{ name: 'bar', location: 'packages/bar', type: PackageType.TSC },
|
|
224
|
+
{ name: 'baz', location: 'packages/baz', type: PackageType.Webpack },
|
|
225
|
+
];
|
|
226
|
+
|
|
186
227
|
const subject = (packages: Package[]) => splitPackagesByType(packages);
|
|
187
228
|
|
|
188
229
|
test('groups packages by type', () => {
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { globSync } from 'glob';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { readJsonSafe } from './read-json';
|
|
4
|
+
import { log } from './log';
|
|
5
|
+
|
|
6
|
+
export interface ProjectPackage extends Record<string, any> {
|
|
7
|
+
name: string;
|
|
8
|
+
location: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function findPackages(): ProjectPackage[] {
|
|
12
|
+
const workspaces = findWorkspaces();
|
|
13
|
+
if (!workspaces) {
|
|
14
|
+
return [];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const result = workspaces
|
|
18
|
+
.flatMap(pattern => globSync(pattern))
|
|
19
|
+
.map(location => {
|
|
20
|
+
const dir = path.resolve(location);
|
|
21
|
+
const file = path.join(dir, 'package.json');
|
|
22
|
+
return { ...readJsonSafe<ProjectPackage>(file), location: dir };
|
|
23
|
+
})
|
|
24
|
+
.filter(({ name }) => name !== undefined) as ProjectPackage[];
|
|
25
|
+
|
|
26
|
+
/* istanbul ignore next: debug only */
|
|
27
|
+
log.debug(
|
|
28
|
+
`find-packages`,
|
|
29
|
+
() => `found packages: ${JSON.stringify(result.map(({ name }) => name))}`
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
return result;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function findWorkspaces() {
|
|
36
|
+
let directory = path.resolve('./');
|
|
37
|
+
do {
|
|
38
|
+
const packageJson = path.resolve(path.join(directory, 'package.json'));
|
|
39
|
+
|
|
40
|
+
log.debug('find-packages', `reading: ${packageJson}`);
|
|
41
|
+
let workspaces = readJsonSafe<{ workspaces: string[] }>(packageJson)?.workspaces;
|
|
42
|
+
|
|
43
|
+
if (!workspaces) {
|
|
44
|
+
const lernaJson = path.resolve(path.join(directory, 'lerna.json'));
|
|
45
|
+
log.debug('find-packages', `reading: ${lernaJson}`);
|
|
46
|
+
workspaces = readJsonSafe<{ packages: string[] }>(lernaJson)?.packages;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (workspaces) {
|
|
50
|
+
workspaces = workspaces.map(
|
|
51
|
+
// Note, glob requires "/" as path separator, even on Windows
|
|
52
|
+
workspace => path.join(directory, workspace).replace(/\\/g, '/')
|
|
53
|
+
);
|
|
54
|
+
/* istanbul ignore next: debug only */
|
|
55
|
+
log.debug('find-packages', () => `found workspaces: ${JSON.stringify(workspaces)}`);
|
|
56
|
+
return workspaces;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
directory = path.resolve(path.join(directory, '../'));
|
|
60
|
+
} while (path.parse(directory).name);
|
|
61
|
+
}
|
|
@@ -71,12 +71,22 @@ type Configuration = {
|
|
|
71
71
|
[key in CommandName]: NodeConfiguration;
|
|
72
72
|
} & NodeConfiguration;
|
|
73
73
|
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
type LocationOrJson = string | Record<string, any>;
|
|
75
|
+
|
|
76
|
+
export function getConfiguration(locationOrJson: LocationOrJson = './'): Configuration {
|
|
77
|
+
const json =
|
|
78
|
+
typeof locationOrJson === 'string'
|
|
79
|
+
? readJson(path.join(locationOrJson, 'package.json'))
|
|
80
|
+
: locationOrJson;
|
|
81
|
+
return json?.cli ?? {};
|
|
76
82
|
}
|
|
77
83
|
|
|
78
|
-
export function getConfigurationSafe(
|
|
79
|
-
|
|
84
|
+
export function getConfigurationSafe(locationOrJson: LocationOrJson = './'): Configuration {
|
|
85
|
+
const json =
|
|
86
|
+
typeof locationOrJson === 'string'
|
|
87
|
+
? readJsonSafe(path.join(locationOrJson, 'package.json'))
|
|
88
|
+
: locationOrJson;
|
|
89
|
+
return json?.cli ?? {};
|
|
80
90
|
}
|
|
81
91
|
|
|
82
92
|
export function getDevServerConfiguration() {
|
|
@@ -114,8 +124,8 @@ export function getStylelintConfiguration() {
|
|
|
114
124
|
return getConfiguration().lint?.stylelint ?? {};
|
|
115
125
|
}
|
|
116
126
|
|
|
117
|
-
export function isBundle(
|
|
118
|
-
return getConfiguration(
|
|
127
|
+
export function isBundle(locationOrJson?: LocationOrJson) {
|
|
128
|
+
return getConfiguration(locationOrJson).webpack !== false;
|
|
119
129
|
}
|
|
120
130
|
|
|
121
131
|
export function isCustomStyleRules() {
|
|
@@ -138,8 +148,8 @@ export function isExposeSharedDependencies() {
|
|
|
138
148
|
return configuration.webpack['expose-shared-dependencies'] === true;
|
|
139
149
|
}
|
|
140
150
|
|
|
141
|
-
export function isLegacy(
|
|
142
|
-
return getConfiguration(
|
|
151
|
+
export function isLegacy(locationOrJson?: LocationOrJson) {
|
|
152
|
+
return getConfiguration(locationOrJson).legacy === true;
|
|
143
153
|
}
|
|
144
154
|
|
|
145
155
|
export function isLegacyRoot() {
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import
|
|
1
|
+
import multimatch from 'multimatch';
|
|
2
|
+
import { findPackages, ProjectPackage } from './find-packages';
|
|
2
3
|
import { isBundle, isLegacy } from './get-configuration';
|
|
3
|
-
import { maybeCreateGitFolder } from './maybe-create-git-folder';
|
|
4
4
|
import { toArray } from './to-array';
|
|
5
|
-
import { log } from './log';
|
|
6
5
|
|
|
7
6
|
export enum PackageType {
|
|
8
7
|
TSC,
|
|
@@ -18,8 +17,6 @@ export interface Package {
|
|
|
18
17
|
location: string;
|
|
19
18
|
}
|
|
20
19
|
|
|
21
|
-
type RawPackage = Omit<Package, 'type'>;
|
|
22
|
-
|
|
23
20
|
interface GetPackagesOptions {
|
|
24
21
|
scope?: string | string[];
|
|
25
22
|
ignore?: string | string[];
|
|
@@ -29,16 +26,13 @@ export function getPackages(options: GetPackagesOptions = {}) {
|
|
|
29
26
|
const scope = toArray(options.scope);
|
|
30
27
|
const ignore = toArray(options.ignore);
|
|
31
28
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
if (!scope.length && !ignore.length) {
|
|
29
|
+
const allPackages = findPackages();
|
|
30
|
+
if (scope.length === 0 && ignore.length === 0) {
|
|
36
31
|
return withMetadata(allPackages);
|
|
37
32
|
}
|
|
38
33
|
|
|
39
|
-
const filteredPackages
|
|
40
|
-
|
|
41
|
-
const graph: Record<string, string[]> = getPackagesGraph();
|
|
34
|
+
const filteredPackages = filterPackages(allPackages, options);
|
|
35
|
+
const graph = buildPackageGraph(allPackages);
|
|
42
36
|
|
|
43
37
|
// We don't need external dependencies
|
|
44
38
|
for (const [vertex, dependencies] of Object.entries(graph)) {
|
|
@@ -68,63 +62,67 @@ export function getPackages(options: GetPackagesOptions = {}) {
|
|
|
68
62
|
return withMetadata(allPackages.filter(({ name }) => used[name]));
|
|
69
63
|
}
|
|
70
64
|
|
|
71
|
-
interface GetPackagesGraphOptions {
|
|
72
|
-
scope?: string | string[];
|
|
73
|
-
ignore?: string | string[];
|
|
74
|
-
}
|
|
75
|
-
|
|
76
65
|
/**
|
|
77
66
|
* Returns packages and their direct dependencies
|
|
78
67
|
*/
|
|
79
|
-
export function getPackagesGraph(options:
|
|
80
|
-
return
|
|
68
|
+
export function getPackagesGraph(options: GetPackagesOptions = {}) {
|
|
69
|
+
return buildPackageGraph(filterPackages(findPackages(), options));
|
|
81
70
|
}
|
|
82
71
|
|
|
83
|
-
function
|
|
84
|
-
|
|
85
|
-
|
|
72
|
+
function buildPackageGraph(packages: ProjectPackage[]) {
|
|
73
|
+
return packages.reduce<Record<string, string[]>>(
|
|
74
|
+
(result, { name, dependencies = {}, devDependencies = {} }) => {
|
|
75
|
+
result[name] = [...Object.keys(dependencies), ...Object.keys(devDependencies)].sort(
|
|
76
|
+
(a, b) => a.localeCompare(b)
|
|
77
|
+
);
|
|
78
|
+
return result;
|
|
79
|
+
},
|
|
80
|
+
{}
|
|
81
|
+
);
|
|
82
|
+
}
|
|
86
83
|
|
|
87
|
-
|
|
88
|
-
if (
|
|
89
|
-
|
|
90
|
-
* Disabled the project graph cache to workaround issue when nx fails with
|
|
91
|
-
* "Error: EPERM: operation not permitted, rename ..." error when multiple
|
|
92
|
-
* processes simultaneously generate the project graph.
|
|
93
|
-
*/
|
|
94
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
95
|
-
execaOptions = { extendEnv: true, env: { NX_CACHE_PROJECT_GRAPH: 'false' } };
|
|
84
|
+
function filterPackages(packages: ProjectPackage[], options: GetPackagesOptions) {
|
|
85
|
+
if (options.ignore === undefined && options.scope === undefined) {
|
|
86
|
+
return packages;
|
|
96
87
|
}
|
|
97
88
|
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
'
|
|
103
|
-
|
|
104
|
-
...ignore.map(v => ['--ignore', v]).flat(),
|
|
105
|
-
options.graph ? '--graph' : '--json',
|
|
106
|
-
],
|
|
107
|
-
execaOptions
|
|
108
|
-
).stdout;
|
|
109
|
-
|
|
110
|
-
try {
|
|
111
|
-
return JSON.parse(output);
|
|
112
|
-
} catch (error) {
|
|
113
|
-
log.error(`error: could not parse lerna output:\n${output}`);
|
|
114
|
-
throw error;
|
|
89
|
+
const patterns = [...toArray(options.scope), ...negate(toArray(options.ignore))];
|
|
90
|
+
|
|
91
|
+
if (patterns.length) {
|
|
92
|
+
if (!options.scope?.length) {
|
|
93
|
+
patterns.unshift('**');
|
|
94
|
+
}
|
|
115
95
|
}
|
|
96
|
+
|
|
97
|
+
const chosen = new Set(
|
|
98
|
+
multimatch(
|
|
99
|
+
packages.map(item => item.name),
|
|
100
|
+
patterns
|
|
101
|
+
)
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
return packages.filter(({ name }) => chosen.has(name));
|
|
116
105
|
}
|
|
117
106
|
|
|
118
|
-
function withMetadata(packages:
|
|
119
|
-
return packages.map(pkg =>
|
|
107
|
+
function withMetadata(packages: ProjectPackage[]): Package[] {
|
|
108
|
+
return packages.map(pkg => {
|
|
109
|
+
const { name, version, location } = pkg;
|
|
110
|
+
return {
|
|
111
|
+
name,
|
|
112
|
+
version,
|
|
113
|
+
private: pkg.private ?? false,
|
|
114
|
+
location,
|
|
115
|
+
type: getPackageType(pkg),
|
|
116
|
+
};
|
|
117
|
+
});
|
|
120
118
|
}
|
|
121
119
|
|
|
122
|
-
function getPackageType(
|
|
123
|
-
if (isLegacy(
|
|
120
|
+
function getPackageType(packageJson: ProjectPackage) {
|
|
121
|
+
if (isLegacy(packageJson)) {
|
|
124
122
|
return PackageType.Legacy;
|
|
125
123
|
}
|
|
126
124
|
|
|
127
|
-
if (isBundle(
|
|
125
|
+
if (isBundle(packageJson)) {
|
|
128
126
|
return PackageType.Webpack;
|
|
129
127
|
}
|
|
130
128
|
|
|
@@ -144,3 +142,7 @@ export function splitPackagesByType(packages: Package[]) {
|
|
|
144
142
|
|
|
145
143
|
return result;
|
|
146
144
|
}
|
|
145
|
+
|
|
146
|
+
function negate(patterns: string[]) {
|
|
147
|
+
return patterns.map(pattern => `!${pattern}`);
|
|
148
|
+
}
|
package/template/lerna.json
CHANGED
|
@@ -8,11 +8,11 @@
|
|
|
8
8
|
"scripts": {},
|
|
9
9
|
"dependencies": {
|
|
10
10
|
"@servicetitan/design-system": "^13.4.3",
|
|
11
|
-
"@servicetitan/hash-browser-router": "^
|
|
12
|
-
"@servicetitan/link-item": "^
|
|
13
|
-
"@servicetitan/log-service": "^
|
|
14
|
-
"@servicetitan/react-ioc": "^
|
|
15
|
-
"@servicetitan/web-components": "^
|
|
11
|
+
"@servicetitan/hash-browser-router": "^24.1.0",
|
|
12
|
+
"@servicetitan/link-item": "^26.1.0",
|
|
13
|
+
"@servicetitan/log-service": "^24.1.0",
|
|
14
|
+
"@servicetitan/react-ioc": "^24.1.0",
|
|
15
|
+
"@servicetitan/web-components": "^24.1.0",
|
|
16
16
|
"axios": "^0.28.1",
|
|
17
17
|
"feature-a": "^0.0.0",
|
|
18
18
|
"feature-b": "^0.0.0",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"react-router-dom": "^5.3.0"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
|
-
"@servicetitan/testing-library": "^
|
|
27
|
+
"@servicetitan/testing-library": "^1.2.0",
|
|
28
28
|
"@testing-library/jest-dom": "^5.17.0",
|
|
29
29
|
"@testing-library/react": "^12.1.5",
|
|
30
30
|
"@testing-library/react-hooks": "^8.0.1",
|