@servicetitan/startup 35.1.0 → 35.2.0-far-1776.1

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 (59) hide show
  1. package/dist/cli/commands/mfe-generate-metadata.d.ts.map +1 -1
  2. package/dist/cli/commands/mfe-generate-metadata.js +6 -1
  3. package/dist/cli/commands/mfe-generate-metadata.js.map +1 -1
  4. package/dist/cli/commands/review/rules/require-compatible-launch-darkly-sdk.d.ts.map +1 -1
  5. package/dist/cli/commands/review/rules/require-compatible-launch-darkly-sdk.js +6 -6
  6. package/dist/cli/commands/review/rules/require-compatible-launch-darkly-sdk.js.map +1 -1
  7. package/dist/cli/commands/review/types.d.ts +2 -5
  8. package/dist/cli/commands/review/types.d.ts.map +1 -1
  9. package/dist/cli/commands/review/types.js.map +1 -1
  10. package/dist/utils/find-package-lock.d.ts +2 -0
  11. package/dist/utils/find-package-lock.d.ts.map +1 -0
  12. package/dist/utils/find-package-lock.js +26 -0
  13. package/dist/utils/find-package-lock.js.map +1 -0
  14. package/dist/utils/find-up.js +4 -4
  15. package/dist/utils/find-up.js.map +1 -1
  16. package/dist/utils/get-package-data.d.ts.map +1 -1
  17. package/dist/utils/get-package-data.js +28 -1
  18. package/dist/utils/get-package-data.js.map +1 -1
  19. package/dist/utils/get-package-version.d.ts +2 -0
  20. package/dist/utils/get-package-version.d.ts.map +1 -0
  21. package/dist/utils/get-package-version.js +26 -0
  22. package/dist/utils/get-package-version.js.map +1 -0
  23. package/dist/utils/index.d.ts +3 -0
  24. package/dist/utils/index.d.ts.map +1 -1
  25. package/dist/utils/index.js +3 -0
  26. package/dist/utils/index.js.map +1 -1
  27. package/dist/utils/resolve-package-lock-entry.d.ts +11 -0
  28. package/dist/utils/resolve-package-lock-entry.d.ts.map +1 -0
  29. package/dist/utils/resolve-package-lock-entry.js +21 -0
  30. package/dist/utils/resolve-package-lock-entry.js.map +1 -0
  31. package/dist/webpack/configs/cache-config.d.ts.map +1 -1
  32. package/dist/webpack/configs/cache-config.js +1 -14
  33. package/dist/webpack/configs/cache-config.js.map +1 -1
  34. package/dist/webpack/configs/utils/get-launchdarkly-sdk-version.d.ts.map +1 -1
  35. package/dist/webpack/configs/utils/get-launchdarkly-sdk-version.js +1 -11
  36. package/dist/webpack/configs/utils/get-launchdarkly-sdk-version.js.map +1 -1
  37. package/dist/webpack/configs/utils/get-web-components-version.d.ts.map +1 -1
  38. package/dist/webpack/configs/utils/get-web-components-version.js +5 -7
  39. package/dist/webpack/configs/utils/get-web-components-version.js.map +1 -1
  40. package/package.json +2 -3
  41. package/src/cli/commands/__tests__/mfe-generate-metadata.test.ts +13 -0
  42. package/src/cli/commands/mfe-generate-metadata.ts +6 -1
  43. package/src/cli/commands/review/rules/require-compatible-launch-darkly-sdk.ts +2 -7
  44. package/src/cli/commands/review/types.ts +3 -8
  45. package/src/utils/__tests__/find-package-lock.test.ts +35 -0
  46. package/src/utils/__tests__/get-package-data.test.ts +97 -26
  47. package/src/utils/__tests__/get-package-version.test.ts +129 -0
  48. package/src/utils/__tests__/resolve-package-lock-entry.test.ts +99 -0
  49. package/src/utils/find-package-lock.ts +10 -0
  50. package/src/utils/find-up.ts +1 -1
  51. package/src/utils/get-package-data.ts +27 -2
  52. package/src/utils/get-package-version.ts +21 -0
  53. package/src/utils/index.ts +3 -0
  54. package/src/utils/resolve-package-lock-entry.ts +25 -0
  55. package/src/webpack/configs/cache-config.ts +1 -10
  56. package/src/webpack/configs/utils/__tests__/get-launchdarkly-sdk-version.test.ts +20 -8
  57. package/src/webpack/configs/utils/__tests__/get-web-components-version.test.ts +30 -10
  58. package/src/webpack/configs/utils/get-launchdarkly-sdk-version.ts +2 -12
  59. package/src/webpack/configs/utils/get-web-components-version.ts +8 -5
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/webpack/configs/utils/get-web-components-version.ts"],"sourcesContent":["import path from 'path';\nimport { readJson } from '../../../utils';\n\nexport function getWebComponentsVersion() {\n return readJson<{ version: string }>(\n path.join(path.dirname(require.resolve('@servicetitan/web-components')), '../package.json')\n ).version;\n}\n"],"names":["getWebComponentsVersion","readJson","path","join","dirname","require","resolve","version"],"mappings":";;;;+BAGgBA;;;eAAAA;;;6DAHC;uBACQ;;;;;;AAElB,SAASA;IACZ,OAAOC,IAAAA,eAAQ,EACXC,aAAI,CAACC,IAAI,CAACD,aAAI,CAACE,OAAO,CAACC,QAAQC,OAAO,CAAC,kCAAkC,oBAC3EC,OAAO;AACb"}
1
+ {"version":3,"sources":["../../../../src/webpack/configs/utils/get-web-components-version.ts"],"sourcesContent":["import { getPackageVersion } from '../../../utils';\n\nexport function getWebComponentsVersion() {\n const version = getPackageVersion('@servicetitan/web-components');\n if (!version) {\n throw new Error(\n 'Unable to resolve @servicetitan/web-components version from package-lock.json'\n );\n }\n return version;\n}\n"],"names":["getWebComponentsVersion","version","getPackageVersion","Error"],"mappings":";;;;+BAEgBA;;;eAAAA;;;uBAFkB;AAE3B,SAASA;IACZ,MAAMC,UAAUC,IAAAA,wBAAiB,EAAC;IAClC,IAAI,CAACD,SAAS;QACV,MAAM,IAAIE,MACN;IAER;IACA,OAAOF;AACX"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@servicetitan/startup",
3
- "version": "35.1.0",
3
+ "version": "35.2.0-far-1776.1",
4
4
  "description": "CLI to create multi-package Lerna projects with TypeScript and React",
5
5
  "homepage": "https://docs.st.dev/docs/frontend/uikit/startup",
6
6
  "repository": {
@@ -145,6 +145,5 @@
145
145
  },
146
146
  "cli": {
147
147
  "webpack": false
148
- },
149
- "gitHead": "f1d49b43f27789cf5cd4bea3e065cd9e33bf5876"
148
+ }
150
149
  }
@@ -35,6 +35,7 @@ describe(`[startup] ${MfeGenerateMetadata.name}`, () => {
35
35
 
36
36
  const mockParentPackageJson = {
37
37
  name: 'parent-package',
38
+ workspaces: ['packages/*'],
38
39
  dependencies: { 'some-dep': '^1.0.0' },
39
40
  };
40
41
 
@@ -116,6 +117,18 @@ describe(`[startup] ${MfeGenerateMetadata.name}`, () => {
116
117
  });
117
118
  });
118
119
 
120
+ describe('when tsconfig.json does not exist', () => {
121
+ beforeEach(() => {
122
+ fs.rmSync('tsconfig.json');
123
+ });
124
+
125
+ test('outputs metadata with empty entrypoints', async () => {
126
+ await subject();
127
+
128
+ expect(getMetadataFromStdout().entrypoints).toEqual({});
129
+ });
130
+ });
131
+
119
132
  describe('with --output option', () => {
120
133
  const outputDir = '/tmp/existing-dir';
121
134
  const outputPath = `${outputDir}/metadata.json`;
@@ -32,7 +32,12 @@ export class MfeGenerateMetadata extends Command<typeof options> {
32
32
 
33
33
  const { output } = this.args;
34
34
 
35
- const { destination } = getFolders();
35
+ let destination: string;
36
+ try {
37
+ ({ destination } = getFolders());
38
+ } catch {
39
+ destination = 'dist';
40
+ }
36
41
  const name = getPackageName();
37
42
  const packageData = getPackageData();
38
43
  const sharedDependencies = loadSharedDependencies(
@@ -1,6 +1,7 @@
1
1
  import chalk from 'chalk';
2
2
  import { execSync } from 'child_process';
3
3
  import { satisfies } from 'semver';
4
+ import { resolvePackageLockEntry } from '../../../../utils';
4
5
  import { FixCategory, Package, PackageError, PackageRule, Project } from '../types';
5
6
  import { applyFilter, checkPackages, setVersion } from '../utils';
6
7
 
@@ -121,13 +122,7 @@ export class RequireCompatibleLaunchDarklySdk implements PackageRule {
121
122
  dependency: string;
122
123
  }) {
123
124
  const { packages } = this.#project.packageLock;
124
- return (
125
- (packageName
126
- ? packages[`node_modules/${packageName}/node_modules/${dependency}`]
127
- : undefined) ??
128
- packages[`node_modules/${dependency}`] ??
129
- {}
130
- );
125
+ return resolvePackageLockEntry(packages, dependency, packageName);
131
126
  }
132
127
 
133
128
  private getTargetJsClientSdkVersion(packageName?: string) {
@@ -1,3 +1,5 @@
1
+ import { PackageLockPackages } from '../../../utils';
2
+
1
3
  export abstract class PackageRule {
2
4
  abstract get id(): string;
3
5
  abstract run(project: Project): PackageError | PackageError[] | undefined;
@@ -9,14 +11,7 @@ export interface Project {
9
11
  dependencies: Dependencies;
10
12
  // Only using this subset of package-lock.json
11
13
  packageLock: {
12
- packages: Record<
13
- string,
14
- {
15
- version: string;
16
- dependencies?: Record<string, string>;
17
- peerDependencies?: Record<string, string>;
18
- }
19
- >;
14
+ packages: PackageLockPackages;
20
15
  location: string;
21
16
  };
22
17
  packages: Package[];
@@ -0,0 +1,35 @@
1
+ import { fs, vol } from 'memfs';
2
+ import path from 'node:path';
3
+ import { findPackageLock } from '../find-package-lock';
4
+
5
+ jest.mock('fs', () => fs);
6
+
7
+ describe(`[startup] ${findPackageLock.name}`, () => {
8
+ afterEach(() => vol.reset());
9
+
10
+ const subject = () => findPackageLock();
11
+
12
+ describe('when package-lock.json exists in cwd', () => {
13
+ beforeEach(() => {
14
+ vol.fromJSON({ './package-lock.json': '{}' });
15
+ });
16
+
17
+ test('returns path', () => {
18
+ expect(subject()).toBe(path.resolve('./package-lock.json'));
19
+ });
20
+ });
21
+
22
+ describe('when package-lock.json exists in parent directory', () => {
23
+ beforeEach(() => {
24
+ vol.fromJSON({ '../package-lock.json': '{}' });
25
+ });
26
+
27
+ test('returns path', () => {
28
+ expect(subject()).toBe(path.resolve('../package-lock.json'));
29
+ });
30
+ });
31
+
32
+ test('returns undefined when no package-lock.json exists', () => {
33
+ expect(subject()).toBeUndefined();
34
+ });
35
+ });
@@ -1,47 +1,118 @@
1
+ import { fs, vol } from 'memfs';
1
2
  import { getPackageData, getPackageDependencyVersion } from '../get-package-data';
2
- import { readJson } from '../read-json';
3
3
 
4
- jest.mock('../read-json', () => ({ readJson: jest.fn() }));
4
+ jest.mock('fs', () => fs);
5
5
 
6
6
  describe(`[startup] Utils`, () => {
7
7
  describe(`${getPackageData.name}`, () => {
8
- const packageJson = './package.json';
9
- const ancestorPackageJson = '../../package.json';
10
- let packages: Record<string, Record<string, any>>;
11
-
12
- beforeEach(() => {
13
- packages = {
14
- [ancestorPackageJson]: { dependencies: { foo: '1.0.0', bar: '2.0.0' } },
15
- [packageJson]: {
8
+ afterEach(() => vol.reset());
9
+
10
+ const subject = () => getPackageData();
11
+
12
+ describe('when root package.json with workspaces exists', () => {
13
+ beforeEach(() => {
14
+ vol.fromJSON({
15
+ './package.json': JSON.stringify({
16
+ name: 'packageName',
17
+ dependencies: { foo: '1.1.0', baz: '3.0.0' },
18
+ }),
19
+ '../package.json': JSON.stringify({
20
+ workspaces: ['packages/*'],
21
+ dependencies: { foo: '1.0.0', bar: '2.0.0' },
22
+ }),
23
+ });
24
+ });
25
+
26
+ test('returns merged dependencies (local overrides root)', () => {
27
+ expect(subject()).toEqual({
16
28
  name: 'packageName',
17
- dependencies: { foo: '1.1.0', baz: '3.0.0' },
18
- },
19
- };
20
- jest.mocked(readJson).mockImplementation(path => packages[path] ?? {});
29
+ dependencies: { foo: '1.1.0', bar: '2.0.0', baz: '3.0.0' },
30
+ });
31
+ });
21
32
  });
22
33
 
23
- const subject = () => getPackageData();
34
+ describe('when no root package.json found', () => {
35
+ beforeEach(() => {
36
+ vol.fromJSON({
37
+ './package.json': JSON.stringify({
38
+ name: 'rootPackage',
39
+ workspaces: ['packages/*'],
40
+ dependencies: { foo: '1.0.0' },
41
+ }),
42
+ });
43
+ });
44
+
45
+ test('returns only local dependencies', () => {
46
+ expect(subject()).toEqual({
47
+ name: 'rootPackage',
48
+ dependencies: { foo: '1.0.0' },
49
+ });
50
+ });
51
+ });
52
+
53
+ describe('when root has lerna.json with packages instead of workspaces', () => {
54
+ beforeEach(() => {
55
+ vol.fromJSON({
56
+ './package.json': JSON.stringify({
57
+ name: 'packageName',
58
+ version: '2.0.0',
59
+ dependencies: { foo: '1.0.0' },
60
+ }),
61
+ '../package.json': JSON.stringify({
62
+ dependencies: { bar: '2.0.0' },
63
+ }),
64
+ '../lerna.json': JSON.stringify({
65
+ packages: ['packages/*'],
66
+ }),
67
+ });
68
+ });
69
+
70
+ test('returns dependencies from root discovered via lerna.json', () => {
71
+ expect(subject()).toEqual({
72
+ name: 'packageName',
73
+ version: '2.0.0',
74
+ dependencies: { bar: '2.0.0', foo: '1.0.0' },
75
+ });
76
+ });
77
+ });
78
+
79
+ describe('when root has lerna.json but no package.json at root level', () => {
80
+ beforeEach(() => {
81
+ vol.fromJSON({
82
+ './package.json': JSON.stringify({
83
+ name: 'packageName',
84
+ version: '3.0.0',
85
+ dependencies: { foo: '1.0.0' },
86
+ }),
87
+ '../lerna.json': JSON.stringify({
88
+ packages: ['packages/*'],
89
+ }),
90
+ });
91
+ });
24
92
 
25
- test(`returns merged dependencies from ${packageJson} and ${ancestorPackageJson}`, () => {
26
- expect(subject()).toEqual({
27
- name: packages[packageJson].name,
28
- dependencies: {
29
- ...packages[ancestorPackageJson].dependencies,
30
- ...packages[packageJson].dependencies,
31
- },
93
+ test('returns only local dependencies', () => {
94
+ expect(subject()).toEqual({
95
+ name: 'packageName',
96
+ version: '3.0.0',
97
+ dependencies: { foo: '1.0.0' },
98
+ });
32
99
  });
33
100
  });
34
101
 
35
- describe(`when ${packageJson} contains sharedDependencies`, () => {
102
+ describe('when package.json contains sharedDependencies', () => {
36
103
  const sharedDependencies = { react: 'SharedDependencies.React' };
37
104
 
38
105
  beforeEach(() => {
39
- Object.assign(packages[packageJson], {
40
- cli: { webpack: { 'shared-dependencies': sharedDependencies } },
106
+ vol.fromJSON({
107
+ './package.json': JSON.stringify({
108
+ name: 'packageName',
109
+ dependencies: { foo: '1.0.0' },
110
+ cli: { webpack: { 'shared-dependencies': sharedDependencies } },
111
+ }),
41
112
  });
42
113
  });
43
114
 
44
- test('returns sharedDependencies', () => {
115
+ test('result contains sharedDependencies', () => {
45
116
  expect(subject()).toEqual(expect.objectContaining({ sharedDependencies }));
46
117
  });
47
118
  });
@@ -0,0 +1,129 @@
1
+ import { fs, vol } from 'memfs';
2
+ import { getPackageVersion } from '../get-package-version';
3
+
4
+ jest.mock('fs', () => fs);
5
+
6
+ describe(`[startup] ${getPackageVersion.name}`, () => {
7
+ afterEach(() => vol.reset());
8
+
9
+ const subject = (packageName: string) => getPackageVersion(packageName);
10
+
11
+ beforeEach(() => {
12
+ vol.fromJSON({
13
+ './package-lock.json': JSON.stringify({
14
+ packages: {
15
+ 'node_modules/@servicetitan/web-components': { version: '1.2.3' },
16
+ },
17
+ }),
18
+ });
19
+ });
20
+
21
+ test('returns version for a found package', () => {
22
+ expect(subject('@servicetitan/web-components')).toBe('1.2.3');
23
+ });
24
+
25
+ describe('when package is not in lock file', () => {
26
+ beforeEach(() => {
27
+ vol.fromJSON({
28
+ './package-lock.json': JSON.stringify({
29
+ packages: {},
30
+ }),
31
+ });
32
+ });
33
+
34
+ test('returns undefined', () => {
35
+ expect(subject('nonexistent-package')).toBeUndefined();
36
+ });
37
+ });
38
+
39
+ describe('when package is a linked workspace package', () => {
40
+ beforeEach(() => {
41
+ vol.fromJSON({
42
+ './package-lock.json': JSON.stringify({
43
+ packages: {
44
+ 'node_modules/@servicetitan/web-components': {
45
+ resolved: 'packages/web-components',
46
+ link: true,
47
+ },
48
+ 'packages/web-components': {
49
+ version: '35.1.0',
50
+ },
51
+ },
52
+ }),
53
+ });
54
+ });
55
+
56
+ test('follows link to resolve version from source path', () => {
57
+ expect(subject('@servicetitan/web-components')).toBe('35.1.0');
58
+ });
59
+ });
60
+
61
+ describe('when entry has link but no resolved path', () => {
62
+ beforeEach(() => {
63
+ vol.fromJSON({
64
+ './package-lock.json': JSON.stringify({
65
+ packages: {
66
+ 'node_modules/some-package': { link: true, version: '1.0.0' },
67
+ },
68
+ }),
69
+ });
70
+ });
71
+
72
+ test('returns entry version', () => {
73
+ expect(subject('some-package')).toBe('1.0.0');
74
+ });
75
+ });
76
+
77
+ describe('when linked package resolved path does not exist in packages', () => {
78
+ beforeEach(() => {
79
+ vol.fromJSON({
80
+ './package-lock.json': JSON.stringify({
81
+ packages: {
82
+ 'node_modules/some-package': {
83
+ resolved: 'packages/some-package',
84
+ link: true,
85
+ },
86
+ },
87
+ }),
88
+ });
89
+ });
90
+
91
+ test('returns undefined', () => {
92
+ expect(subject('some-package')).toBeUndefined();
93
+ });
94
+ });
95
+
96
+ describe('when lock file contains invalid JSON', () => {
97
+ beforeEach(() => {
98
+ vol.fromJSON({
99
+ './package-lock.json': 'not valid json',
100
+ });
101
+ });
102
+
103
+ test('returns undefined', () => {
104
+ expect(subject('some-package')).toBeUndefined();
105
+ });
106
+ });
107
+
108
+ describe('when lock file has no packages property', () => {
109
+ beforeEach(() => {
110
+ vol.fromJSON({
111
+ './package-lock.json': JSON.stringify({}),
112
+ });
113
+ });
114
+
115
+ test('returns undefined', () => {
116
+ expect(subject('some-package')).toBeUndefined();
117
+ });
118
+ });
119
+
120
+ describe('when package-lock.json does not exist', () => {
121
+ beforeEach(() => {
122
+ fs.rmSync('package-lock.json');
123
+ });
124
+
125
+ test('returns undefined', () => {
126
+ expect(subject('@servicetitan/web-components')).toBeUndefined();
127
+ });
128
+ });
129
+ });
@@ -0,0 +1,99 @@
1
+ import { PackageLockPackages, resolvePackageLockEntry } from '../resolve-package-lock-entry';
2
+
3
+ describe(`[startup] ${resolvePackageLockEntry.name}`, () => {
4
+ let packages: PackageLockPackages;
5
+ let packageName: string;
6
+ let scope: string | undefined;
7
+
8
+ beforeEach(() => {
9
+ packages = {};
10
+ packageName = 'foo';
11
+ });
12
+
13
+ const subject = () => resolvePackageLockEntry(packages, packageName, scope);
14
+
15
+ describe('when package exists at top level', () => {
16
+ beforeEach(() => {
17
+ packages['node_modules/foo'] = { version: '1.0.0' };
18
+ });
19
+
20
+ test('resolves the entry', () => {
21
+ expect(subject()).toEqual({ version: '1.0.0' });
22
+ });
23
+ });
24
+
25
+ describe('when package exists both at top level and under a scope', () => {
26
+ beforeEach(() => {
27
+ packages['node_modules/foo'] = { version: '1.0.0' };
28
+ packages['node_modules/parent/node_modules/foo'] = { version: '2.0.0' };
29
+ scope = undefined;
30
+ });
31
+
32
+ describe('when scope is provided', () => {
33
+ beforeEach(() => {
34
+ scope = 'parent';
35
+ });
36
+
37
+ test('resolves scoped entry', () => {
38
+ expect(subject()).toEqual({ version: '2.0.0' });
39
+ });
40
+ });
41
+
42
+ test('resolves top-level entry', () => {
43
+ expect(subject()).toEqual({ version: '1.0.0' });
44
+ });
45
+ });
46
+
47
+ describe('when package has dependencies and peerDependencies', () => {
48
+ beforeEach(() => {
49
+ packages['node_modules/foo'] = {
50
+ version: '1.0.0',
51
+ dependencies: { bar: '^1.0.0' },
52
+ peerDependencies: { baz: '>=2.0.0' },
53
+ };
54
+ });
55
+
56
+ test('returns the full entry', () => {
57
+ expect(subject()).toEqual({
58
+ version: '1.0.0',
59
+ dependencies: { bar: '^1.0.0' },
60
+ peerDependencies: { baz: '>=2.0.0' },
61
+ });
62
+ });
63
+ });
64
+
65
+ describe('when package is a linked workspace package', () => {
66
+ beforeEach(() => {
67
+ packages['node_modules/foo'] = { link: true, resolved: 'packages/foo' };
68
+ packages['packages/foo'] = { version: '3.0.0' };
69
+ });
70
+
71
+ test('follows link to resolved path', () => {
72
+ expect(subject()).toEqual({ version: '3.0.0' });
73
+ });
74
+
75
+ describe('when resolved entry does not exist', () => {
76
+ beforeEach(() => {
77
+ delete packages['packages/foo'];
78
+ });
79
+
80
+ test('returns empty object', () => {
81
+ expect(subject()).toEqual({});
82
+ });
83
+ });
84
+
85
+ describe('when resolved property is missing', () => {
86
+ beforeEach(() => {
87
+ delete packages['node_modules/foo'].resolved;
88
+ });
89
+
90
+ test('returns the entry as-is', () => {
91
+ expect(subject()).toEqual({ link: true });
92
+ });
93
+ });
94
+ });
95
+
96
+ test('returns empty object', () => {
97
+ expect(subject()).toEqual({});
98
+ });
99
+ });
@@ -0,0 +1,10 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { findUp } from './find-up';
4
+
5
+ export function findPackageLock() {
6
+ return findUp(directory => {
7
+ const lockFile = path.resolve(directory, 'package-lock.json');
8
+ return fs.existsSync(lockFile) ? lockFile : undefined;
9
+ });
10
+ }
@@ -1,4 +1,4 @@
1
- import path from 'path';
1
+ import path from 'node:path';
2
2
 
3
3
  export function findUp<T>(callback: (directory: string) => T | undefined) {
4
4
  let directory = path.resolve('./');
@@ -1,4 +1,6 @@
1
- import { readJson } from './read-json';
1
+ import path from 'node:path';
2
+ import { findUp } from './find-up';
3
+ import { readJson, readJsonSafe } from './read-json';
2
4
 
3
5
  interface PackageData {
4
6
  name: string;
@@ -14,13 +16,36 @@ export function getPackageData(): PackageData {
14
16
  name: packageJson.name,
15
17
  version: packageJson.version,
16
18
  dependencies: {
17
- ...readJson('../../package.json').dependencies,
19
+ ...findRootDependencies(),
18
20
  ...packageJson.dependencies,
19
21
  },
20
22
  sharedDependencies: packageJson.cli?.webpack?.['shared-dependencies'],
21
23
  };
22
24
  }
23
25
 
26
+ function findRootDependencies() {
27
+ const cwd = path.resolve('./');
28
+ const root = findUp(directory => {
29
+ if (directory === cwd) {
30
+ return undefined;
31
+ }
32
+ const pkgPath = path.join(directory, 'package.json');
33
+ const pkg = readJsonSafe<{ workspaces?: string[]; dependencies?: Record<string, string> }>(
34
+ pkgPath
35
+ );
36
+ if (pkg?.workspaces) {
37
+ return pkg;
38
+ }
39
+
40
+ const lernaPath = path.join(directory, 'lerna.json');
41
+ if (readJsonSafe<{ packages?: string[] }>(lernaPath)?.packages) {
42
+ return pkg ?? {};
43
+ }
44
+ });
45
+
46
+ return root?.dependencies ?? {};
47
+ }
48
+
24
49
  export function getPackageDependencyVersion(dependency: string, defaultVersion?: string) {
25
50
  try {
26
51
  const { version } = require(`${dependency}/package.json`);
@@ -0,0 +1,21 @@
1
+ import { findPackageLock } from './find-package-lock';
2
+ import { readJsonSafe } from './read-json';
3
+ import { PackageLockPackages, resolvePackageLockEntry } from './resolve-package-lock-entry';
4
+
5
+ interface PackageLock {
6
+ packages: PackageLockPackages;
7
+ }
8
+
9
+ export function getPackageVersion(packageName: string) {
10
+ const lockFilePath = findPackageLock();
11
+ if (!lockFilePath) {
12
+ return undefined;
13
+ }
14
+
15
+ const lockFile = readJsonSafe<PackageLock>(lockFilePath);
16
+ if (!lockFile) {
17
+ return undefined;
18
+ }
19
+
20
+ return resolvePackageLockEntry(lockFile.packages, packageName).version;
21
+ }
@@ -1,3 +1,4 @@
1
+ export * from './find-package-lock';
1
2
  export * from './find-packages';
2
3
  export * from './find-up';
3
4
  export * from './format-duration';
@@ -10,6 +11,7 @@ export * from './get-folders';
10
11
  export * from './get-jest-config';
11
12
  export * from './get-package-data';
12
13
  export * from './get-package-name';
14
+ export * from './get-package-version';
13
15
  export * from './get-packages';
14
16
  export * from './get-startup-version';
15
17
  export * from './get-tsconfig';
@@ -21,4 +23,5 @@ export * from './omit';
21
23
  export * from './pick';
22
24
  export * from './prettify';
23
25
  export * from './read-json';
26
+ export * from './resolve-package-lock-entry';
24
27
  export * from './to-array';
@@ -0,0 +1,25 @@
1
+ interface PackageLockEntry {
2
+ version?: string;
3
+ resolved?: string;
4
+ link?: boolean;
5
+ dependencies?: Record<string, string>;
6
+ peerDependencies?: Record<string, string>;
7
+ }
8
+
9
+ export type PackageLockPackages = Record<string, PackageLockEntry>;
10
+
11
+ export function resolvePackageLockEntry(
12
+ packages: PackageLockPackages | undefined,
13
+ packageName: string,
14
+ scope?: string
15
+ ) {
16
+ const entry =
17
+ (scope ? packages?.[`node_modules/${scope}/node_modules/${packageName}`] : undefined) ??
18
+ packages?.[`node_modules/${packageName}`];
19
+
20
+ if (entry?.link && entry.resolved) {
21
+ return packages?.[entry.resolved] ?? {};
22
+ }
23
+
24
+ return entry ?? {};
25
+ }
@@ -1,7 +1,5 @@
1
- import fs from 'fs';
2
- import path from 'path';
3
1
  import { Configuration } from 'webpack';
4
- import { findUp } from '../../utils';
2
+ import { findPackageLock } from '../../utils';
5
3
  import { Context, Overrides } from './types';
6
4
 
7
5
  type Config = Configuration['cache'];
@@ -28,10 +26,3 @@ export function cacheConfig(context: Context, _overrides: Overrides): Result {
28
26
  infrastructureLogging: { level: 'error' },
29
27
  };
30
28
  }
31
-
32
- function findPackageLock() {
33
- return findUp(directory => {
34
- const lockFile = path.resolve(path.join(directory), 'package-lock.json');
35
- return fs.existsSync(lockFile) ? lockFile : undefined;
36
- });
37
- }