@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.
Files changed (32) hide show
  1. package/dist/cli/commands/mfe-publish.d.ts.map +1 -1
  2. package/dist/cli/commands/mfe-publish.js +5 -1
  3. package/dist/cli/commands/mfe-publish.js.map +1 -1
  4. package/dist/utils/find-packages.d.ts +6 -0
  5. package/dist/utils/find-packages.d.ts.map +1 -0
  6. package/dist/utils/find-packages.js +52 -0
  7. package/dist/utils/find-packages.js.map +1 -0
  8. package/dist/utils/get-configuration.d.ts +5 -4
  9. package/dist/utils/get-configuration.d.ts.map +1 -1
  10. package/dist/utils/get-configuration.js +15 -9
  11. package/dist/utils/get-configuration.js.map +1 -1
  12. package/dist/utils/get-packages.d.ts +1 -5
  13. package/dist/utils/get-packages.d.ts.map +1 -1
  14. package/dist/utils/get-packages.js +41 -38
  15. package/dist/utils/get-packages.js.map +1 -1
  16. package/package.json +13 -8
  17. package/src/cli/commands/__tests__/mfe-package-publish.test.ts +8 -0
  18. package/src/cli/commands/mfe-publish.ts +8 -1
  19. package/src/utils/__tests__/get-configuration.test.ts +30 -8
  20. package/src/utils/__tests__/get-packages.test.ts +162 -121
  21. package/src/utils/find-packages.ts +61 -0
  22. package/src/utils/get-configuration.ts +18 -8
  23. package/src/utils/get-packages.ts +55 -53
  24. package/template/lerna.json +1 -2
  25. package/template/packages/application/package.json +6 -6
  26. package/template-react18/packages/application/package.json +6 -6
  27. package/dist/utils/maybe-create-git-folder.d.ts +0 -10
  28. package/dist/utils/maybe-create-git-folder.d.ts.map +0 -1
  29. package/dist/utils/maybe-create-git-folder.js +0 -25
  30. package/dist/utils/maybe-create-git-folder.js.map +0 -1
  31. package/src/utils/__tests__/maybe-create-git-folder.test.ts +0 -41
  32. package/src/utils/maybe-create-git-folder.ts +0 -18
@@ -1,8 +1,5 @@
1
- import execa from 'execa';
2
- import { isBundle, isLegacy } from '../get-configuration';
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('execa', () => ({ sync: jest.fn() }));
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
- dependencies = undefined;
68
-
69
- jest.mocked(isBundle).mockImplementation(
70
- location => findPackageByLocation(location)?.type === PackageType.Webpack
71
- );
72
- jest.mocked(isLegacy).mockImplementation(
73
- location => findPackageByLocation(location)?.type === PackageType.Legacy
74
- );
75
- // @ts-expect-error because implementation doesn't match all exec.sync signatures
76
- jest.mocked(execa.sync).mockImplementation((_: string, args: string[]): any => {
77
- return { stdout: JSON.stringify(resolvePackages(args)) };
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
- const subject = () => getPackages(options);
82
-
83
- test('returns lerna packages with type metadata', () => {
84
- expect(subject()).toEqual(packages);
85
- expectExecaToBeCalledWith('npx lerna la --json');
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
- test('conditionally creates .git folder', () => {
89
- subject();
90
-
91
- expect(maybeCreateGitFolder).toHaveBeenCalled();
92
- });
86
+ describe('with "scope"', () => {
87
+ beforeEach(() => (options = { scope: 'foo' }));
93
88
 
94
- describe('when running on Windows', () => {
95
- const originalPlatform = process.platform;
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
- afterEach(() => {
101
- Object.defineProperty(process, 'platform', { value: originalPlatform });
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
- test('sets NX_CACHE_PROJECT_GRAPH=false', () => {
105
- subject();
111
+ describe('when "scope" is a glob expression', () => {
112
+ beforeEach(() => (options = { scope: 'ba*' }));
106
113
 
107
- expectExecaToBeCalledWith('npx lerna la --json', {
108
- extendEnv: true,
109
- // eslint-disable-next-line @typescript-eslint/naming-convention
110
- env: { NX_CACHE_PROJECT_GRAPH: 'false' },
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.each(['scope', 'ignore'])('with "%s"', option => {
116
- beforeEach(() => (options = { [option]: 'foo' }));
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
- test(`passes "${option}" option to lerna`, () => {
119
- subject();
130
+ describe('when "ignore" is a glob expression', () => {
131
+ beforeEach(() => (options = { ignore: ['ba*'] }));
120
132
 
121
- expectExecaToBeCalledWith(`npx lerna la --${option} foo --json`);
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
- describe('with "scope"', () => {
126
- beforeEach(() => (options = { scope: 'foo' }));
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
- describe('when filtered package has dependencies', () => {
133
- beforeEach(() => (dependencies = { ...packageGraph(), foo: ['bar', 'external'] }));
147
+ describe('with no packages', () => {
148
+ beforeEach(() => vol.fromJSON({ 'package.json': JSON.stringify({}) }));
134
149
 
135
- test('also returns internal dependencies', () => {
136
- expect(subject()).toEqual(
137
- packages.filter(({ name }) => ['foo', 'bar'].includes(name))
138
- );
139
- });
140
- });
150
+ itReturnsEmptyArray();
141
151
  });
142
152
 
143
- describe('when execa returns unexpected output', () => {
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
- test('throws an error and logs the output', () => {
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
- jest.mocked(execa.sync).mockImplementation((): any => ({
163
- stdout: JSON.stringify(packageGraph),
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 lerna package graph', () => {
170
- expect(subject()).toEqual(packageGraph);
171
- expectExecaToBeCalledWith('npx lerna la --graph');
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.each(['scope', 'ignore'])('with "%s"', option => {
175
- beforeEach(() => (options = { [option]: 'foo' }));
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
- test(`passes "${option}" option to lerna`, () => {
178
- subject();
211
+ describe('with "ignore"', () => {
212
+ beforeEach(() => (options = { ignore: ['foo', 'bar'] }));
179
213
 
180
- expectExecaToBeCalledWith(`npx lerna la --${option} foo --graph`);
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
- export function getConfiguration(location = './'): Configuration {
75
- return readJson(path.join(location, 'package.json')).cli ?? {};
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(location = './'): Configuration {
79
- return readJsonSafe(path.join(location, 'package.json'))?.cli ?? {};
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(location?: string) {
118
- return getConfiguration(location).webpack !== false;
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(location?: string) {
142
- return getConfiguration(location).legacy === true;
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 execa from 'execa';
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
- maybeCreateGitFolder();
33
- const allPackages: RawPackage[] = listPackages();
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: RawPackage[] = listPackages(options);
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: GetPackagesGraphOptions = {}): Record<string, string[]> {
80
- return listPackages({ ...options, graph: true });
68
+ export function getPackagesGraph(options: GetPackagesOptions = {}) {
69
+ return buildPackageGraph(filterPackages(findPackages(), options));
81
70
  }
82
71
 
83
- function listPackages(options: GetPackagesOptions & { graph?: boolean } = {}) {
84
- const scope = toArray(options.scope);
85
- const ignore = toArray(options.ignore);
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
- let execaOptions: execa.SyncOptions | undefined;
88
- if (process.platform === 'win32') {
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 output = execa.sync(
99
- 'npx',
100
- [
101
- 'lerna',
102
- 'la',
103
- ...scope.map(v => ['--scope', v]).flat(),
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: RawPackage[]): Package[] {
119
- return packages.map(pkg => ({ ...pkg, type: getPackageType(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({ location }: RawPackage) {
123
- if (isLegacy(location)) {
120
+ function getPackageType(packageJson: ProjectPackage) {
121
+ if (isLegacy(packageJson)) {
124
122
  return PackageType.Legacy;
125
123
  }
126
124
 
127
- if (isBundle(location)) {
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
+ }
@@ -1,5 +1,4 @@
1
1
  {
2
2
  "packages": ["packages/*"],
3
- "version": "0.0.0",
4
- "useWorkspaces": true
3
+ "version": "0.0.0"
5
4
  }
@@ -8,11 +8,11 @@
8
8
  "scripts": {},
9
9
  "dependencies": {
10
10
  "@servicetitan/design-system": "^13.4.3",
11
- "@servicetitan/hash-browser-router": "^23.1.0",
12
- "@servicetitan/link-item": "^25.9.0",
13
- "@servicetitan/log-service": "^23.1.0",
14
- "@servicetitan/react-ioc": "^23.1.0",
15
- "@servicetitan/web-components": "^23.1.0",
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": "^0.3.0",
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",