@servicetitan/startup 36.1.1 → 36.1.2-canary.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/bin/_run.js +77 -0
- package/bin/cpx.js +3 -0
- package/bin/eslint.js +3 -0
- package/bin/jest.js +3 -0
- package/bin/js-yaml.js +3 -0
- package/bin/json5.js +3 -0
- package/bin/kendo-ui-license.js +3 -0
- package/bin/lerna.js +3 -0
- package/bin/lessc.js +3 -0
- package/bin/patch-package.js +3 -0
- package/bin/prettier.js +3 -0
- package/bin/sass.js +3 -0
- package/bin/semver.js +3 -0
- package/bin/spack.js +3 -0
- package/bin/stylelint.js +3 -0
- package/bin/swc.js +3 -0
- package/bin/swcx.js +3 -0
- package/bin/tcm.js +3 -0
- package/bin/ts-jest.js +3 -0
- package/bin/ts-node-cwd.js +3 -0
- package/bin/ts-node-esm.js +3 -0
- package/bin/ts-node-script.js +3 -0
- package/bin/ts-node-transpile-only.js +3 -0
- package/bin/ts-node.js +3 -0
- package/bin/ts-script.js +3 -0
- package/bin/tsc.js +3 -0
- package/bin/tsserver.js +3 -0
- package/bin/vitest.js +3 -0
- package/bin/webpack-bundle-analyzer.js +3 -0
- package/bin/webpack-dev-server.js +3 -0
- package/bin/webpack.js +3 -0
- package/dist/cli/commands/clean.d.ts +1 -0
- package/dist/cli/commands/clean.d.ts.map +1 -1
- package/dist/cli/commands/clean.js +18 -8
- package/dist/cli/commands/clean.js.map +1 -1
- package/dist/cli/commands/coverage-finalize.d.ts +16 -0
- package/dist/cli/commands/coverage-finalize.d.ts.map +1 -0
- package/dist/cli/commands/coverage-finalize.js +41 -0
- package/dist/cli/commands/coverage-finalize.js.map +1 -0
- package/dist/cli/commands/install.js +1 -1
- package/dist/cli/commands/install.js.map +1 -1
- package/dist/cli/commands/kendo-ui-license.js +1 -2
- package/dist/cli/commands/kendo-ui-license.js.map +1 -1
- package/dist/cli/commands/prepare-package.js +2 -2
- package/dist/cli/commands/prepare-package.js.map +1 -1
- package/dist/cli/commands/registry/coverage-finalize.d.ts +5 -0
- package/dist/cli/commands/registry/coverage-finalize.d.ts.map +1 -0
- package/dist/cli/commands/registry/coverage-finalize.js +17 -0
- package/dist/cli/commands/registry/coverage-finalize.js.map +1 -0
- package/dist/cli/commands/review/review.d.ts.map +1 -1
- package/dist/cli/commands/review/review.js +8 -7
- package/dist/cli/commands/review/review.js.map +1 -1
- package/dist/cli/commands/review/rules/require-compatible-launch-darkly-sdk.d.ts +0 -1
- package/dist/cli/commands/review/rules/require-compatible-launch-darkly-sdk.d.ts.map +1 -1
- package/dist/cli/commands/review/rules/require-compatible-launch-darkly-sdk.js +12 -18
- package/dist/cli/commands/review/rules/require-compatible-launch-darkly-sdk.js.map +1 -1
- package/dist/cli/commands/review/rules/require-project-version-in-root-node-modules.d.ts.map +1 -1
- package/dist/cli/commands/review/rules/require-project-version-in-root-node-modules.js +9 -4
- package/dist/cli/commands/review/rules/require-project-version-in-root-node-modules.js.map +1 -1
- package/dist/cli/commands/review/types.d.ts +2 -8
- package/dist/cli/commands/review/types.d.ts.map +1 -1
- package/dist/cli/commands/review/types.js.map +1 -1
- package/dist/cli/utils/index.d.ts +0 -3
- package/dist/cli/utils/index.d.ts.map +1 -1
- package/dist/cli/utils/index.js +0 -3
- package/dist/cli/utils/index.js.map +1 -1
- package/dist/cypress/config/webpack-config.js +0 -1
- package/dist/cypress/config/webpack-config.js.map +1 -1
- package/dist/storybook-config/webpack-final.d.ts.map +1 -1
- package/dist/storybook-config/webpack-final.js +0 -6
- package/dist/storybook-config/webpack-final.js.map +1 -1
- package/dist/utils/default-excludes.d.ts +14 -0
- package/dist/utils/default-excludes.d.ts.map +1 -0
- package/dist/utils/default-excludes.js +23 -0
- package/dist/utils/default-excludes.js.map +1 -0
- package/dist/utils/find-packages.d.ts.map +1 -1
- package/dist/utils/find-packages.js +15 -0
- package/dist/utils/find-packages.js.map +1 -1
- package/dist/utils/get-coverage-aliases.d.ts +23 -0
- package/dist/utils/get-coverage-aliases.d.ts.map +1 -0
- package/dist/utils/get-coverage-aliases.js +41 -0
- package/dist/utils/get-coverage-aliases.js.map +1 -0
- package/dist/utils/get-coverage-source-patterns.d.ts +21 -0
- package/dist/utils/get-coverage-source-patterns.d.ts.map +1 -0
- package/dist/utils/get-coverage-source-patterns.js +39 -0
- package/dist/utils/get-coverage-source-patterns.js.map +1 -0
- package/dist/utils/get-jest-config.d.ts.map +1 -1
- package/dist/utils/get-jest-config.js +2 -1
- package/dist/utils/get-jest-config.js.map +1 -1
- package/dist/utils/prettify.js +1 -1
- package/dist/utils/prettify.js.map +1 -1
- package/dist/webpack/configs/cache-config.d.ts.map +1 -1
- package/dist/webpack/configs/cache-config.js +3 -1
- package/dist/webpack/configs/cache-config.js.map +1 -1
- package/dist/webpack/configs/rules/ts-coverage-rules.d.ts +22 -0
- package/dist/webpack/configs/rules/ts-coverage-rules.d.ts.map +1 -0
- package/dist/webpack/configs/rules/ts-coverage-rules.js +48 -0
- package/dist/webpack/configs/rules/ts-coverage-rules.js.map +1 -0
- package/eslint/config.mjs +2 -0
- package/package.json +54 -18
- package/src/__tests__/postinstall.test.ts +173 -0
- package/src/cli/commands/__tests__/clean.test.ts +30 -12
- package/src/cli/commands/__tests__/install.test.ts +3 -1
- package/src/cli/commands/__tests__/kendo-ui-license.test.ts +4 -4
- package/src/cli/commands/__tests__/prepare-package.test.ts +2 -3
- package/src/cli/commands/clean.ts +18 -9
- package/src/cli/commands/install.ts +1 -1
- package/src/cli/commands/kendo-ui-license.ts +1 -1
- package/src/cli/commands/prepare-package.ts +1 -1
- package/src/cli/commands/review/__mocks__/index.ts +1 -0
- package/src/cli/commands/review/__mocks__/mock-package-manager.ts +20 -0
- package/src/cli/commands/review/__tests__/review.test.ts +23 -31
- package/src/cli/commands/review/review.ts +11 -7
- package/src/cli/commands/review/rules/__mocks__/mock-project.ts +2 -1
- package/src/cli/commands/review/rules/__tests__/require-compatible-launch-darkly-sdk.test.ts +39 -49
- package/src/cli/commands/review/rules/__tests__/require-project-version-in-root-node-modules.test.ts +35 -22
- package/src/cli/commands/review/rules/require-compatible-launch-darkly-sdk.ts +13 -28
- package/src/cli/commands/review/rules/require-project-version-in-root-node-modules.ts +10 -3
- package/src/cli/commands/review/types.ts +3 -12
- package/src/cli/utils/index.ts +10 -3
- package/src/cypress/config/__tests__/webpack-config.test.ts +0 -9
- package/src/cypress/config/webpack-config.ts +0 -1
- package/src/postinstall.js +106 -14
- package/src/scripts/__tests__/generate-bin-wrappers.test.ts +226 -0
- package/src/scripts/generate-bin-wrappers.js +205 -0
- package/src/storybook-config/__tests__/webpack-final.test.ts +0 -25
- package/src/storybook-config/webpack-final.ts +0 -4
- package/src/utils/__tests__/get-jest-config.test.ts +3 -1
- package/src/utils/__tests__/get-packages.test.ts +47 -2
- package/src/utils/__tests__/prettify.test.ts +1 -1
- package/src/utils/find-packages.ts +16 -0
- package/src/utils/get-jest-config.ts +4 -1
- package/src/utils/prettify.ts +1 -1
- package/src/webpack/configs/cache-config.ts +3 -1
package/src/cli/commands/review/rules/__tests__/require-compatible-launch-darkly-sdk.test.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import { execSync } from 'child_process';
|
|
3
|
-
import { expectCalls } from '../../__mocks__';
|
|
3
|
+
import { expectCalls, MockPackageManager } from '../../__mocks__';
|
|
4
4
|
import { FixCategory, Package, PackageError, Project, ReviewConfiguration } from '../../types';
|
|
5
5
|
import { setVersion } from '../../utils';
|
|
6
6
|
import { mockConfig, mockPackages, mockProject } from '../__mocks__';
|
|
@@ -15,23 +15,45 @@ jest.mock('../../utils', () => ({
|
|
|
15
15
|
const JS_CLIENT_SDK = 'launchdarkly-js-client-sdk';
|
|
16
16
|
const LD_SERVICE = '@servicetitan/launchdarkly-service';
|
|
17
17
|
|
|
18
|
+
interface LockFile {
|
|
19
|
+
packages: Record<string, { version?: string; dependencies?: Record<string, string> }>;
|
|
20
|
+
location: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
18
23
|
describe(`[startup] Review ${RequireCompatibleLaunchDarklySdk.name}`, () => {
|
|
19
24
|
const id = 'require-compatible-launch-darkly-sdk';
|
|
20
25
|
const rule = new RequireCompatibleLaunchDarklySdk();
|
|
26
|
+
const mockPackageManager = new MockPackageManager();
|
|
21
27
|
let config: ReviewConfiguration;
|
|
22
28
|
let dependencies: Project['dependencies'];
|
|
23
|
-
let
|
|
29
|
+
let lockFile: LockFile;
|
|
24
30
|
let packages: Package[];
|
|
25
31
|
|
|
32
|
+
beforeAll(() => {
|
|
33
|
+
jest.spyOn(mockPackageManager, 'getResolvedVersion').mockImplementation(
|
|
34
|
+
({ dependency }) => lockFile.packages[dependency]?.version
|
|
35
|
+
);
|
|
36
|
+
jest.spyOn(mockPackageManager, 'getDeclaredVersion').mockImplementation(
|
|
37
|
+
({ declaredBy, dependency }) =>
|
|
38
|
+
lockFile.packages[declaredBy]?.dependencies?.[dependency]
|
|
39
|
+
);
|
|
40
|
+
});
|
|
41
|
+
|
|
26
42
|
beforeEach(() => {
|
|
27
43
|
config = {};
|
|
28
44
|
dependencies = {};
|
|
29
45
|
packages = [];
|
|
30
|
-
|
|
46
|
+
lockFile = { packages: {}, location: mockPackageManager.lockFileName };
|
|
31
47
|
jest.clearAllMocks();
|
|
32
48
|
});
|
|
33
49
|
|
|
34
|
-
const project = () =>
|
|
50
|
+
const project = () =>
|
|
51
|
+
mockProject({
|
|
52
|
+
config,
|
|
53
|
+
dependencies,
|
|
54
|
+
packageManager: mockPackageManager as any,
|
|
55
|
+
packages,
|
|
56
|
+
});
|
|
35
57
|
|
|
36
58
|
const subject = () => rule.run(project());
|
|
37
59
|
|
|
@@ -74,11 +96,11 @@ describe(`[startup] Review ${RequireCompatibleLaunchDarklySdk.name}`, () => {
|
|
|
74
96
|
beforeEach(() => {
|
|
75
97
|
dependencies = { [JS_CLIENT_SDK]: { [sdkVersions.package]: [packageName] } };
|
|
76
98
|
packages = mockPackages(dependencies);
|
|
77
|
-
|
|
78
|
-
[
|
|
99
|
+
lockFile.packages = {
|
|
100
|
+
[JS_CLIENT_SDK]: {
|
|
79
101
|
version: sdkVersions.lockfile,
|
|
80
102
|
},
|
|
81
|
-
[
|
|
103
|
+
[LD_SERVICE]: {
|
|
82
104
|
version: '1.0.0',
|
|
83
105
|
dependencies: { [JS_CLIENT_SDK]: sdkVersions.ldService },
|
|
84
106
|
},
|
|
@@ -112,47 +134,15 @@ describe(`[startup] Review ${RequireCompatibleLaunchDarklySdk.name}`, () => {
|
|
|
112
134
|
itReturnsNothing();
|
|
113
135
|
});
|
|
114
136
|
|
|
115
|
-
function mergePackageLock(
|
|
116
|
-
key
|
|
117
|
-
data: Partial<(typeof packageLock)['packages'][string]>
|
|
118
|
-
) {
|
|
119
|
-
packageLock.packages[key] = { ...packageLock.packages[key], ...data };
|
|
137
|
+
function mergePackageLock(key: string, data: Partial<LockFile['packages'][string]>) {
|
|
138
|
+
lockFile.packages[key] = { ...lockFile.packages[key], ...data };
|
|
120
139
|
}
|
|
121
140
|
|
|
122
|
-
describe(`when ${JS_CLIENT_SDK} is private package dependency`, () => {
|
|
123
|
-
const privateLockfileVersion = '1.2.0';
|
|
124
|
-
|
|
125
|
-
beforeEach(() => {
|
|
126
|
-
mergePackageLock(`node_modules/${packageName}/node_modules/${JS_CLIENT_SDK}`, {
|
|
127
|
-
version: privateLockfileVersion,
|
|
128
|
-
});
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
itReturnsError({
|
|
132
|
-
details: `${chalk.yellow(privateLockfileVersion)} does not match ${sdkVersions.ldService}`,
|
|
133
|
-
});
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
describe(`when ${JS_CLIENT_SDK} is peer dependency of ${LD_SERVICE}`, () => {
|
|
137
|
-
const peerLDServiceVersion = '1.0.2';
|
|
138
|
-
|
|
139
|
-
beforeEach(() => {
|
|
140
|
-
mergePackageLock(`node_modules/${LD_SERVICE}`, {
|
|
141
|
-
peerDependencies: { [JS_CLIENT_SDK]: peerLDServiceVersion },
|
|
142
|
-
dependencies: undefined,
|
|
143
|
-
});
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
itReturnsError({
|
|
147
|
-
details: `${chalk.yellow(sdkVersions.lockfile)} does not match ${peerLDServiceVersion}`,
|
|
148
|
-
});
|
|
149
|
-
});
|
|
150
|
-
|
|
151
141
|
describe(`when ${LD_SERVICE} dependency is a compatible range`, () => {
|
|
152
142
|
const compatibleRange = `^${sdkVersions.ldService}`;
|
|
153
143
|
|
|
154
144
|
beforeEach(() => {
|
|
155
|
-
mergePackageLock(
|
|
145
|
+
mergePackageLock(LD_SERVICE, {
|
|
156
146
|
dependencies: { [JS_CLIENT_SDK]: compatibleRange },
|
|
157
147
|
});
|
|
158
148
|
});
|
|
@@ -164,7 +154,7 @@ describe(`[startup] Review ${RequireCompatibleLaunchDarklySdk.name}`, () => {
|
|
|
164
154
|
const incompatibleRange = `<${sdkVersions.ldService}`;
|
|
165
155
|
|
|
166
156
|
beforeEach(() => {
|
|
167
|
-
mergePackageLock(
|
|
157
|
+
mergePackageLock(LD_SERVICE, {
|
|
168
158
|
dependencies: { [JS_CLIENT_SDK]: incompatibleRange },
|
|
169
159
|
});
|
|
170
160
|
});
|
|
@@ -198,7 +188,7 @@ describe(`[startup] Review ${RequireCompatibleLaunchDarklySdk.name}`, () => {
|
|
|
198
188
|
|
|
199
189
|
describe(`when ${JS_CLIENT_SDK} is not in lockfile`, () => {
|
|
200
190
|
beforeEach(() => {
|
|
201
|
-
delete
|
|
191
|
+
delete lockFile.packages[JS_CLIENT_SDK];
|
|
202
192
|
});
|
|
203
193
|
|
|
204
194
|
itReturnsNothing();
|
|
@@ -206,7 +196,7 @@ describe(`[startup] Review ${RequireCompatibleLaunchDarklySdk.name}`, () => {
|
|
|
206
196
|
|
|
207
197
|
describe(`when ${LD_SERVICE} is not in lockfile`, () => {
|
|
208
198
|
beforeEach(() => {
|
|
209
|
-
delete
|
|
199
|
+
delete lockFile.packages[LD_SERVICE];
|
|
210
200
|
});
|
|
211
201
|
|
|
212
202
|
itReturnsNothing();
|
|
@@ -217,11 +207,11 @@ describe(`[startup] Review ${RequireCompatibleLaunchDarklySdk.name}`, () => {
|
|
|
217
207
|
const sdkVersions = { lockfile: '1.1.0', ldService: '1.0.1' };
|
|
218
208
|
|
|
219
209
|
beforeEach(() => {
|
|
220
|
-
|
|
221
|
-
[
|
|
210
|
+
lockFile.packages = {
|
|
211
|
+
[JS_CLIENT_SDK]: {
|
|
222
212
|
version: sdkVersions.lockfile,
|
|
223
213
|
},
|
|
224
|
-
[
|
|
214
|
+
[LD_SERVICE]: {
|
|
225
215
|
version: '1.0.0',
|
|
226
216
|
dependencies: { [JS_CLIENT_SDK]: sdkVersions.ldService },
|
|
227
217
|
},
|
|
@@ -231,7 +221,7 @@ describe(`[startup] Review ${RequireCompatibleLaunchDarklySdk.name}`, () => {
|
|
|
231
221
|
itReturnsError(() => ({
|
|
232
222
|
message: `project depends on an incompatible version of ${JS_CLIENT_SDK}`,
|
|
233
223
|
details: `${chalk.yellow(sdkVersions.lockfile)} does not match ${sdkVersions.ldService}`,
|
|
234
|
-
location:
|
|
224
|
+
location: lockFile.location,
|
|
235
225
|
fixable: FixCategory.lockFile,
|
|
236
226
|
}));
|
|
237
227
|
|
package/src/cli/commands/review/rules/__tests__/require-project-version-in-root-node-modules.test.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import { execSync } from 'child_process';
|
|
3
|
-
import { expectCalls } from '../../__mocks__';
|
|
3
|
+
import { expectCalls, MockPackageManager } from '../../__mocks__';
|
|
4
4
|
import { FixCategory, Package, Project, ReviewConfiguration } from '../../types';
|
|
5
5
|
import { mockConfig, mockPackages, mockProject } from '../__mocks__';
|
|
6
6
|
import { RequireProjectVersionInRootNodeModules } from '../require-project-version-in-root-node-modules';
|
|
@@ -10,20 +10,35 @@ jest.mock('child_process', () => ({ execSync: jest.fn() }));
|
|
|
10
10
|
describe(`[startup] Review ${RequireProjectVersionInRootNodeModules.name}`, () => {
|
|
11
11
|
const id = 'require-project-version-in-root-node-modules';
|
|
12
12
|
const rule = new RequireProjectVersionInRootNodeModules();
|
|
13
|
+
const mockPackageManager = new MockPackageManager();
|
|
13
14
|
let config: ReviewConfiguration;
|
|
14
15
|
let dependencies: Project['dependencies'];
|
|
15
|
-
let
|
|
16
|
+
let resolvedRootVersions: Record<string, string>;
|
|
16
17
|
let packages: Package[];
|
|
17
18
|
|
|
19
|
+
beforeAll(() => {
|
|
20
|
+
jest.spyOn(mockPackageManager, 'getResolvedVersion').mockImplementation(
|
|
21
|
+
({ workspacePackage, dependency }) =>
|
|
22
|
+
workspacePackage === '' ? resolvedRootVersions[dependency] : undefined
|
|
23
|
+
);
|
|
24
|
+
});
|
|
25
|
+
|
|
18
26
|
beforeEach(() => {
|
|
19
27
|
config = {};
|
|
20
28
|
dependencies = { foo: { '~1.0.0': ['lib'] } };
|
|
21
29
|
packages = [{ name: 'root', location: '.' }, ...mockPackages(dependencies)];
|
|
22
|
-
|
|
30
|
+
resolvedRootVersions = {};
|
|
31
|
+
mockPackageManager.command = 'npm';
|
|
23
32
|
jest.clearAllMocks();
|
|
24
33
|
});
|
|
25
34
|
|
|
26
|
-
const project = () =>
|
|
35
|
+
const project = () =>
|
|
36
|
+
mockProject({
|
|
37
|
+
config,
|
|
38
|
+
dependencies,
|
|
39
|
+
packageManager: mockPackageManager as any,
|
|
40
|
+
packages,
|
|
41
|
+
});
|
|
27
42
|
|
|
28
43
|
const subject = () => rule.run(project());
|
|
29
44
|
|
|
@@ -39,7 +54,7 @@ describe(`[startup] Review ${RequireProjectVersionInRootNodeModules.name}`, () =
|
|
|
39
54
|
id,
|
|
40
55
|
message: 'project has unexpected version in root node_modules',
|
|
41
56
|
details,
|
|
42
|
-
location:
|
|
57
|
+
location: mockPackageManager.lockFileName,
|
|
43
58
|
fixable: FixCategory.lockFile,
|
|
44
59
|
})
|
|
45
60
|
);
|
|
@@ -61,17 +76,13 @@ describe(`[startup] Review ${RequireProjectVersionInRootNodeModules.name}`, () =
|
|
|
61
76
|
}
|
|
62
77
|
|
|
63
78
|
describe('when root matches project version', () => {
|
|
64
|
-
beforeEach(() => {
|
|
65
|
-
packageLock.packages = { 'node_modules/foo': { version: '1.0.1' } };
|
|
66
|
-
});
|
|
79
|
+
beforeEach(() => (resolvedRootVersions = { foo: '1.0.1' }));
|
|
67
80
|
|
|
68
81
|
itReturnsNothing();
|
|
69
82
|
});
|
|
70
83
|
|
|
71
84
|
describe('when root does not match project version', () => {
|
|
72
|
-
beforeEach(() => {
|
|
73
|
-
packageLock.packages = { 'node_modules/foo': { version: '1.1.0' } };
|
|
74
|
-
});
|
|
85
|
+
beforeEach(() => (resolvedRootVersions = { foo: '1.1.0' }));
|
|
75
86
|
|
|
76
87
|
itReturnsError(`${chalk.yellow('foo@1.1.0')} does not match ${chalk.yellow('~1.0.0')}`);
|
|
77
88
|
|
|
@@ -81,6 +92,12 @@ describe(`[startup] Review ${RequireProjectVersionInRootNodeModules.name}`, () =
|
|
|
81
92
|
itReturnsNothing();
|
|
82
93
|
});
|
|
83
94
|
|
|
95
|
+
describe('when package manager is pnpm', () => {
|
|
96
|
+
beforeEach(() => (mockPackageManager.command = 'pnpm'));
|
|
97
|
+
|
|
98
|
+
itReturnsNothing();
|
|
99
|
+
});
|
|
100
|
+
|
|
84
101
|
test('fixes error', () => {
|
|
85
102
|
fixSubject();
|
|
86
103
|
|
|
@@ -113,7 +130,7 @@ describe(`[startup] Review ${RequireProjectVersionInRootNodeModules.name}`, () =
|
|
|
113
130
|
});
|
|
114
131
|
|
|
115
132
|
describe('with no root version', () => {
|
|
116
|
-
beforeEach(() => (
|
|
133
|
+
beforeEach(() => (resolvedRootVersions = {}));
|
|
117
134
|
|
|
118
135
|
itReturnsNothing();
|
|
119
136
|
});
|
|
@@ -122,17 +139,13 @@ describe(`[startup] Review ${RequireProjectVersionInRootNodeModules.name}`, () =
|
|
|
122
139
|
beforeEach(() => (dependencies = { foo: { '~1.0.0': ['lib1'], '^2.0.0': ['lib2'] } }));
|
|
123
140
|
|
|
124
141
|
describe('when root matches version', () => {
|
|
125
|
-
beforeEach(() => {
|
|
126
|
-
packageLock.packages = { 'node_modules/foo': { version: '2.1.0' } };
|
|
127
|
-
});
|
|
142
|
+
beforeEach(() => (resolvedRootVersions = { foo: '2.1.0' }));
|
|
128
143
|
|
|
129
144
|
itReturnsNothing();
|
|
130
145
|
});
|
|
131
146
|
|
|
132
147
|
describe('when root does not match any version', () => {
|
|
133
|
-
beforeEach(() => {
|
|
134
|
-
packageLock.packages = { 'node_modules/foo': { version: '1.1.0' } };
|
|
135
|
-
});
|
|
148
|
+
beforeEach(() => (resolvedRootVersions = { foo: '1.1.0' }));
|
|
136
149
|
|
|
137
150
|
itReturnsError(
|
|
138
151
|
`${chalk.yellow('foo@1.1.0')} does not match ${chalk.yellow('~1.0.0, ^2.0.0')}`
|
|
@@ -156,10 +169,10 @@ describe(`[startup] Review ${RequireProjectVersionInRootNodeModules.name}`, () =
|
|
|
156
169
|
bar: { '>=2 <3': ['lib2'] },
|
|
157
170
|
baz: { '>=3 <4': ['lib3'] },
|
|
158
171
|
};
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
172
|
+
resolvedRootVersions = {
|
|
173
|
+
foo: '1.1.0', // error
|
|
174
|
+
bar: '2.0.1', // ok
|
|
175
|
+
baz: '4.0.0', // error
|
|
163
176
|
};
|
|
164
177
|
});
|
|
165
178
|
|
|
@@ -70,8 +70,8 @@ export class RequireCompatibleLaunchDarklySdk implements PackageRule {
|
|
|
70
70
|
return;
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
const actualVersion = this.getActualJsClientSdkVersion(
|
|
74
|
-
const targetVersion = this.getTargetJsClientSdkVersion(
|
|
73
|
+
const actualVersion = this.getActualJsClientSdkVersion(location);
|
|
74
|
+
const targetVersion = this.getTargetJsClientSdkVersion(location);
|
|
75
75
|
if (actualVersion && targetVersion && !satisfies(actualVersion, targetVersion)) {
|
|
76
76
|
return this.createError({
|
|
77
77
|
message: `package "${name}" depends on an incompatible version of ${JS_CLIENT_SDK}`,
|
|
@@ -87,7 +87,7 @@ export class RequireCompatibleLaunchDarklySdk implements PackageRule {
|
|
|
87
87
|
if (actualVersion && targetVersion && !satisfies(actualVersion, targetVersion)) {
|
|
88
88
|
return this.createError({
|
|
89
89
|
message: `project depends on an incompatible version of ${JS_CLIENT_SDK}`,
|
|
90
|
-
location: this.#project.
|
|
90
|
+
location: this.#project.packageManager.lockFileName,
|
|
91
91
|
data: { actualVersion, targetVersion },
|
|
92
92
|
});
|
|
93
93
|
}
|
|
@@ -109,33 +109,18 @@ export class RequireCompatibleLaunchDarklySdk implements PackageRule {
|
|
|
109
109
|
return `${chalk.yellow(actualVersion)} does not match ${targetVersion}`;
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
-
private getActualJsClientSdkVersion(
|
|
113
|
-
return this.
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
packageName,
|
|
118
|
-
dependency,
|
|
119
|
-
}: {
|
|
120
|
-
packageName?: string;
|
|
121
|
-
dependency: string;
|
|
122
|
-
}) {
|
|
123
|
-
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
|
-
);
|
|
112
|
+
private getActualJsClientSdkVersion(workspacePackage = '') {
|
|
113
|
+
return this.#project.packageManager.getResolvedVersion({
|
|
114
|
+
workspacePackage,
|
|
115
|
+
dependency: JS_CLIENT_SDK,
|
|
116
|
+
});
|
|
131
117
|
}
|
|
132
118
|
|
|
133
|
-
private getTargetJsClientSdkVersion(
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
119
|
+
private getTargetJsClientSdkVersion(workspacePackage = '') {
|
|
120
|
+
return this.#project.packageManager.getDeclaredVersion({
|
|
121
|
+
workspacePackage,
|
|
122
|
+
declaredBy: LD_SERVICE,
|
|
123
|
+
dependency: JS_CLIENT_SDK,
|
|
137
124
|
});
|
|
138
|
-
|
|
139
|
-
return dependencies[JS_CLIENT_SDK] ?? peerDependencies[JS_CLIENT_SDK];
|
|
140
125
|
}
|
|
141
126
|
}
|
|
@@ -21,6 +21,11 @@ export class RequireProjectVersionInRootNodeModules implements PackageRule {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
run(project: Project): PackageError<ErrorData> | undefined {
|
|
24
|
+
// pnpm isolates each package's dependencies, so root version mismatches are harmless
|
|
25
|
+
if (project.packageManager.command === 'pnpm') {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
24
29
|
const mismatches = this.findMismatches(project);
|
|
25
30
|
if (!mismatches.length) {
|
|
26
31
|
return;
|
|
@@ -31,7 +36,7 @@ export class RequireProjectVersionInRootNodeModules implements PackageRule {
|
|
|
31
36
|
message: 'project has unexpected version in root node_modules',
|
|
32
37
|
details: this.formatDetails(mismatches),
|
|
33
38
|
fixable: FixCategory.lockFile,
|
|
34
|
-
location: project.
|
|
39
|
+
location: project.packageManager.lockFileName,
|
|
35
40
|
data: { mismatches },
|
|
36
41
|
};
|
|
37
42
|
}
|
|
@@ -80,9 +85,11 @@ export class RequireProjectVersionInRootNodeModules implements PackageRule {
|
|
|
80
85
|
}
|
|
81
86
|
|
|
82
87
|
private findMismatches(project: Project) {
|
|
83
|
-
const { packageLock } = project;
|
|
84
88
|
return this.getDependencies(project).reduce((result, [name, versions]) => {
|
|
85
|
-
const rootVersion =
|
|
89
|
+
const rootVersion = project.packageManager.getResolvedVersion({
|
|
90
|
+
workspacePackage: '',
|
|
91
|
+
dependency: name,
|
|
92
|
+
});
|
|
86
93
|
if (rootVersion) {
|
|
87
94
|
const projectVersions = Object.keys(versions);
|
|
88
95
|
if (!projectVersions.some(version => satisfies(rootVersion, version))) {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { PackageManager } from '@servicetitan/install';
|
|
2
|
+
|
|
1
3
|
export abstract class PackageRule {
|
|
2
4
|
abstract get id(): string;
|
|
3
5
|
abstract run(project: Project): PackageError | PackageError[] | undefined;
|
|
@@ -7,18 +9,7 @@ export abstract class PackageRule {
|
|
|
7
9
|
export interface Project {
|
|
8
10
|
config: ReviewConfiguration;
|
|
9
11
|
dependencies: Dependencies;
|
|
10
|
-
|
|
11
|
-
packageLock: {
|
|
12
|
-
packages: Record<
|
|
13
|
-
string,
|
|
14
|
-
{
|
|
15
|
-
version: string;
|
|
16
|
-
dependencies?: Record<string, string>;
|
|
17
|
-
peerDependencies?: Record<string, string>;
|
|
18
|
-
}
|
|
19
|
-
>;
|
|
20
|
-
location: string;
|
|
21
|
-
};
|
|
12
|
+
packageManager: PackageManager;
|
|
22
13
|
packages: Package[];
|
|
23
14
|
}
|
|
24
15
|
|
package/src/cli/utils/index.ts
CHANGED
|
@@ -5,10 +5,17 @@ export * from './cli-git';
|
|
|
5
5
|
export * from './cli-npm';
|
|
6
6
|
export * from './cli-os';
|
|
7
7
|
export * from './compile';
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
/*
|
|
9
|
+
* These modules eagerly load heavy dependencies (less, sass). Importing them
|
|
10
|
+
* from the barrel causes every test that touches cli/utils to pay that cost
|
|
11
|
+
* and spams "Less has finished and no sheets were loaded" in test output.
|
|
12
|
+
* Import directly from the module instead (e.g., import { copyFiles } from './copy-files').
|
|
13
|
+
*
|
|
14
|
+
* export * from './compile-less';
|
|
15
|
+
* export * from './compile-sass';
|
|
16
|
+
* export * from './copy-files';
|
|
17
|
+
*/
|
|
10
18
|
export * from './constants';
|
|
11
|
-
export * from './copy-files';
|
|
12
19
|
export * from './eslint';
|
|
13
20
|
export * from './get-module-type';
|
|
14
21
|
export * from './is-module-installed';
|
|
@@ -45,15 +45,6 @@ describe(`[startup/cypress-config] ${webpackConfig.name}`, () => {
|
|
|
45
45
|
);
|
|
46
46
|
});
|
|
47
47
|
|
|
48
|
-
test('enables code coverage', () => {
|
|
49
|
-
subject();
|
|
50
|
-
|
|
51
|
-
expect(rulesConfig).toHaveBeenCalledWith(
|
|
52
|
-
expect.objectContaining({ codeCoverage: true }),
|
|
53
|
-
expect.anything()
|
|
54
|
-
);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
48
|
test('includes all rules from rulesConfig', () => {
|
|
58
49
|
const result = subject();
|
|
59
50
|
|
package/src/postinstall.js
CHANGED
|
@@ -1,26 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Applies patch-package patches after startup is installed in a consumer project.
|
|
3
|
+
*
|
|
4
|
+
* Under npm, patch-package finds packages directly in node_modules/. Under pnpm,
|
|
5
|
+
* packages live in the .pnpm store and aren't at their expected node_modules/<pkg>
|
|
6
|
+
* paths. This script bridges the gap by creating temporary symlinks so
|
|
7
|
+
* patch-package can locate and patch them, then cleans up the symlinks afterward.
|
|
8
|
+
*
|
|
9
|
+
* Only runs when startup is installed inside a project directory (not globally
|
|
10
|
+
* or in an npx cache). Failures are logged as warnings to avoid blocking installs.
|
|
11
|
+
*/
|
|
1
12
|
const { execFileSync } = require('child_process');
|
|
2
13
|
const fs = require('fs');
|
|
3
14
|
const path = require('path');
|
|
4
15
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Parse patch filenames and resolve each package's install location in the
|
|
18
|
+
* .pnpm store. Returns an array of { pkg, link, target } for packages that
|
|
19
|
+
* need a temporary symlink (i.e., not already at node_modules/<pkg>).
|
|
20
|
+
*
|
|
21
|
+
* Expected filename format (patch-package convention):
|
|
22
|
+
* <name>+<version>.patch — e.g., jsdom+26.1.0.patch
|
|
23
|
+
* <name>+<version>.dev.patch — e.g., jsdom+26.1.0.dev.patch
|
|
24
|
+
* @<scope>+<name>+<version>.patch — e.g., @babel+core+7.28.5.patch
|
|
25
|
+
*/
|
|
26
|
+
function getPnpmPatchTargets(cwd, patchDir) {
|
|
27
|
+
const pnpmDir = path.join(cwd, 'node_modules', '.pnpm');
|
|
28
|
+
if (!fs.existsSync(pnpmDir)) {
|
|
29
|
+
return [];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const targets = [];
|
|
33
|
+
for (const file of fs.readdirSync(patchDir).filter(f => f.endsWith('.patch'))) {
|
|
34
|
+
const match = file.match(/^(?<name>.+)\+\d/);
|
|
35
|
+
if (!match?.groups) {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const { name } = match.groups;
|
|
40
|
+
const pkg = name.replace(/\+/, '/'); // @scope+name -> @scope/name
|
|
41
|
+
const link = path.join(cwd, 'node_modules', pkg);
|
|
42
|
+
if (fs.existsSync(link)) {
|
|
43
|
+
// already exists
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const entry = fs.readdirSync(pnpmDir).find(e => e.startsWith(`${name}@`));
|
|
48
|
+
if (!entry) {
|
|
49
|
+
// package not installed (e.g., .dev patch in production install)
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
targets.push({
|
|
54
|
+
pkg,
|
|
55
|
+
link,
|
|
56
|
+
target: path.join(pnpmDir, entry, 'node_modules', pkg),
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
return targets;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/*
|
|
63
|
+
* For pnpm, patch-package can't find packages because they live in the .pnpm
|
|
64
|
+
* store instead of node_modules/<pkg>. We create temporary symlinks so
|
|
65
|
+
* patch-package can find them, then clean up after.
|
|
66
|
+
*/
|
|
67
|
+
function addPnpmSymlinks(targets) {
|
|
68
|
+
for (const { link, target } of targets) {
|
|
69
|
+
fs.mkdirSync(path.dirname(link), { recursive: true });
|
|
70
|
+
fs.symlinkSync(target, link);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function removePnpmSymlinks(targets) {
|
|
75
|
+
for (const { link } of targets) {
|
|
76
|
+
fs.unlinkSync(link);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function main() {
|
|
81
|
+
const cwd = process.env.INIT_CWD;
|
|
82
|
+
const patchDir = path.resolve('patches');
|
|
83
|
+
const relativePatchDir = path.relative(cwd, patchDir);
|
|
84
|
+
const isInstalledInCurrentDirectory = patchDir.startsWith(path.resolve(cwd));
|
|
10
85
|
|
|
11
|
-
try {
|
|
12
86
|
// Only apply patches when installed in project (as opposed to globally or in npx cache)
|
|
13
|
-
if (fs.existsSync('package.json')
|
|
14
|
-
|
|
87
|
+
if (!fs.existsSync('package.json') || !isInstalledInCurrentDirectory) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const patchTargets = getPnpmPatchTargets(cwd, patchDir);
|
|
92
|
+
addPnpmSymlinks(patchTargets);
|
|
93
|
+
try {
|
|
94
|
+
execFileSync('patch-package', ['--error-on-fail', '--patch-dir', relativePatchDir], {
|
|
15
95
|
cwd,
|
|
16
96
|
shell: process.platform === 'win32',
|
|
17
97
|
stdio: 'inherit',
|
|
18
98
|
});
|
|
99
|
+
} finally {
|
|
100
|
+
removePnpmSymlinks(patchTargets);
|
|
19
101
|
}
|
|
20
|
-
} catch (e) {
|
|
21
|
-
/*
|
|
22
|
-
* To avoid disrupting workflows only log warning when postinstall fails.
|
|
23
|
-
* To see detailed error, run npm with --foreground-scripts option.
|
|
24
|
-
*/
|
|
25
|
-
console.warn(`postinstall failed`, e); // eslint-disable-line no-console
|
|
26
102
|
}
|
|
103
|
+
|
|
104
|
+
// Only run when executed directly (e.g., `node postinstall.js`), not when imported (e.g., in tests).
|
|
105
|
+
/* istanbul ignore next */
|
|
106
|
+
if (require.main === module) {
|
|
107
|
+
try {
|
|
108
|
+
main();
|
|
109
|
+
} catch (e) {
|
|
110
|
+
/*
|
|
111
|
+
* To avoid disrupting workflows only log warning when postinstall fails.
|
|
112
|
+
* To see detailed error, run npm with --foreground-scripts option.
|
|
113
|
+
*/
|
|
114
|
+
console.warn(`postinstall failed`, e); // eslint-disable-line no-console
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
module.exports = { main };
|