@wise/wds-codemods 0.0.1-experimental-2eb5228 → 0.0.1-experimental-0d8d466
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/.changeset/better-impalas-drop.md +1 -1
- package/.changeset/quick-mails-joke.md +128 -0
- package/.github/actions/test/action.yml +23 -0
- package/DEVELOPER.md +783 -0
- package/README.md +157 -14
- package/dist/index.js +10 -377
- package/dist/index.js.map +1 -1
- package/dist/transforms/button.d.ts +1 -5
- package/dist/transforms/button.js +96 -147
- package/dist/transforms/button.js.map +1 -1
- package/package.json +5 -7
- package/src/__tests__/runCodemod.test.ts +15 -57
- package/src/runCodemod.ts +1 -65
- package/src/transforms/button/__tests__/button.test.tsx +3 -25
- package/src/transforms/button/button.ts +126 -161
- package/src/transforms/helpers/createTestTransform.ts +2 -43
- package/src/transforms/helpers/index.ts +1 -1
- package/src/utils/__tests__/getOptions.test.ts +20 -69
- package/src/utils/getOptions.ts +2 -17
- package/src/transforms/helpers/__tests__/packageValidation.test.ts +0 -45
- package/src/transforms/helpers/packageValidation.ts +0 -53
- package/src/utils/__tests__/hasPackageVersion.test.ts +0 -190
- package/src/utils/hasPackageVersion.ts +0 -459
|
@@ -1,59 +1,18 @@
|
|
|
1
1
|
import type { Options, Transform } from 'jscodeshift';
|
|
2
2
|
import { applyTransform, type TestOptions } from 'jscodeshift/src/testUtils';
|
|
3
3
|
|
|
4
|
-
interface PackageRequirement {
|
|
5
|
-
name: string;
|
|
6
|
-
version: string;
|
|
7
|
-
found?: boolean; // Optional - defaults to true
|
|
8
|
-
}
|
|
9
|
-
|
|
10
4
|
/**
|
|
11
5
|
* This function creates a test transform function that applies a given transformer to the input code.
|
|
12
6
|
* It uses the 'tsx' parser to support both TypeScript and JSX syntax.
|
|
13
7
|
*
|
|
14
8
|
* The transform function is used to modify the input code based on the provided transformer.
|
|
15
|
-
*
|
|
16
|
-
* @param transformer - The jscodeshift transformer function
|
|
17
|
-
* @param packageRequirements - Array of package requirements to mock (defaults to empty array)
|
|
18
9
|
*/
|
|
19
|
-
function createTestTransform(
|
|
20
|
-
|
|
21
|
-
packageRequirements: PackageRequirement[] = [],
|
|
22
|
-
) {
|
|
23
|
-
// Create mock package results from requirements
|
|
24
|
-
const packageResults: Record<string, boolean> = {};
|
|
25
|
-
packageRequirements.forEach((pkg) => {
|
|
26
|
-
const key = `${pkg.name}@${pkg.version}`;
|
|
27
|
-
packageResults[key] = pkg.found ?? true; // Default to found unless explicitly set to false
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
const options: Options = {
|
|
31
|
-
// Only add packageResults if we have package requirements
|
|
32
|
-
...(packageRequirements.length > 0 ? { packageResults: JSON.stringify(packageResults) } : {}),
|
|
33
|
-
};
|
|
34
|
-
|
|
10
|
+
function createTestTransform(transformer: Transform) {
|
|
11
|
+
const options: Options = {};
|
|
35
12
|
// Use 'tsx' parser to support both TypeScript and JSX syntax
|
|
36
13
|
const testOptions: TestOptions = { parser: 'tsx' };
|
|
37
|
-
|
|
38
14
|
return (input: { path?: string; source: string }) =>
|
|
39
15
|
applyTransform(transformer, options, input, testOptions);
|
|
40
16
|
}
|
|
41
17
|
|
|
42
|
-
// Convenience functions for common use cases
|
|
43
|
-
export const createTestTransformWithPackage = (
|
|
44
|
-
transformer: Transform,
|
|
45
|
-
packageName: string,
|
|
46
|
-
version: string,
|
|
47
|
-
) => {
|
|
48
|
-
return createTestTransform(transformer, [{ name: packageName, version }]);
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
export const createTestTransformWithoutPackage = (
|
|
52
|
-
transformer: Transform,
|
|
53
|
-
packageName: string,
|
|
54
|
-
version: string,
|
|
55
|
-
) => {
|
|
56
|
-
return createTestTransform(transformer, [{ name: packageName, version, found: false }]);
|
|
57
|
-
};
|
|
58
|
-
|
|
59
18
|
export default createTestTransform;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
export { default as createTestTransform } from './createTestTransform';
|
|
1
2
|
export { default as hasImport } from './hasImport';
|
|
2
3
|
export { default as processIconChildren } from './iconUtils';
|
|
3
4
|
export * from './jsxElementUtils';
|
|
4
5
|
export * from './jsxReportingUtils';
|
|
5
|
-
export * from './packageValidation';
|
|
@@ -30,7 +30,6 @@ describe('getOptions', () => {
|
|
|
30
30
|
print: true,
|
|
31
31
|
gitignore: true,
|
|
32
32
|
ignorePattern: undefined,
|
|
33
|
-
isMonorepo: false,
|
|
34
33
|
});
|
|
35
34
|
});
|
|
36
35
|
|
|
@@ -45,13 +44,6 @@ describe('getOptions', () => {
|
|
|
45
44
|
];
|
|
46
45
|
const options = await getOptions(transformFiles);
|
|
47
46
|
expect(options.ignorePattern).toBe('node_modules/**');
|
|
48
|
-
expect(options.isMonorepo).toBe(false);
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it('should parse --monorepo argument from CLI', async () => {
|
|
52
|
-
process.argv = ['node', 'script.js', 'fileA.js', './packages', '--monorepo'];
|
|
53
|
-
const options = await getOptions(transformFiles);
|
|
54
|
-
expect(options.isMonorepo).toBe(true);
|
|
55
47
|
});
|
|
56
48
|
|
|
57
49
|
it('should parse --gitignore and --no-gitignore arguments from CLI and prioritize --gitignore', async () => {
|
|
@@ -68,12 +60,12 @@ describe('getOptions', () => {
|
|
|
68
60
|
(list as jest.Mock).mockResolvedValue('fileB.ts');
|
|
69
61
|
(input as jest.Mock).mockResolvedValueOnce('./output').mockResolvedValueOnce('node_modules/**');
|
|
70
62
|
(confirm as jest.Mock)
|
|
71
|
-
.mockResolvedValueOnce(true)
|
|
72
63
|
.mockResolvedValueOnce(true)
|
|
73
64
|
.mockResolvedValueOnce(false)
|
|
74
65
|
.mockResolvedValueOnce(true);
|
|
75
66
|
|
|
76
|
-
const
|
|
67
|
+
const optionsPromise = getOptions(transformFiles);
|
|
68
|
+
const options = await optionsPromise;
|
|
77
69
|
|
|
78
70
|
expect(input).toHaveBeenCalledWith({
|
|
79
71
|
message: 'Enter ignore pattern(s) (comma separated) or leave empty:',
|
|
@@ -85,35 +77,22 @@ describe('getOptions', () => {
|
|
|
85
77
|
});
|
|
86
78
|
expect(options.ignorePattern).toBe('node_modules/**');
|
|
87
79
|
expect(options.gitignore).toBe(true);
|
|
88
|
-
expect(options.isMonorepo).toBe(true);
|
|
89
80
|
});
|
|
90
81
|
|
|
91
|
-
it('should
|
|
92
|
-
|
|
93
|
-
(
|
|
94
|
-
|
|
95
|
-
.mockResolvedValueOnce(true)
|
|
96
|
-
.mockResolvedValueOnce(true)
|
|
97
|
-
.mockResolvedValueOnce(false)
|
|
98
|
-
.mockResolvedValueOnce(true);
|
|
99
|
-
|
|
100
|
-
const options = await getOptions(transformFiles);
|
|
82
|
+
it('should throw an error if transform file is invalid via command line', async () => {
|
|
83
|
+
process.argv = ['node', 'script.js', 'invalid.js', './dist'];
|
|
84
|
+
await expect(getOptions(transformFiles)).rejects.toThrow('Invalid transform file specified.');
|
|
85
|
+
});
|
|
101
86
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
});
|
|
106
|
-
expect(options.isMonorepo).toBe(true);
|
|
87
|
+
it('should throw an error if target path is empty via command line', async () => {
|
|
88
|
+
process.argv = ['node', 'script.js', 'fileA.js', ''];
|
|
89
|
+
await expect(getOptions(transformFiles)).rejects.toThrow('Target path cannot be empty.');
|
|
107
90
|
});
|
|
108
91
|
|
|
109
92
|
it('should prompt for transform file, target path, dry mode, and print when no arguments are provided', async () => {
|
|
110
93
|
(list as jest.Mock).mockResolvedValue('fileB.ts');
|
|
111
|
-
(input as jest.Mock).
|
|
112
|
-
(confirm as jest.Mock)
|
|
113
|
-
.mockResolvedValueOnce(false)
|
|
114
|
-
.mockResolvedValueOnce(true)
|
|
115
|
-
.mockResolvedValueOnce(false)
|
|
116
|
-
.mockResolvedValueOnce(true);
|
|
94
|
+
(input as jest.Mock).mockResolvedValue('./output');
|
|
95
|
+
(confirm as jest.Mock).mockResolvedValueOnce(true).mockResolvedValueOnce(false);
|
|
117
96
|
|
|
118
97
|
const options = await getOptions(transformFiles);
|
|
119
98
|
|
|
@@ -138,20 +117,15 @@ describe('getOptions', () => {
|
|
|
138
117
|
targetPath: './output',
|
|
139
118
|
dry: true,
|
|
140
119
|
print: false,
|
|
141
|
-
gitignore:
|
|
142
|
-
ignorePattern: '',
|
|
143
|
-
isMonorepo: false,
|
|
120
|
+
gitignore: undefined,
|
|
121
|
+
ignorePattern: './output',
|
|
144
122
|
});
|
|
145
123
|
});
|
|
146
124
|
|
|
147
125
|
it('should handle target path validation error', async () => {
|
|
148
126
|
(list as jest.Mock).mockResolvedValue('fileA.js');
|
|
149
|
-
(input as jest.Mock).mockResolvedValueOnce('
|
|
150
|
-
(confirm as jest.Mock)
|
|
151
|
-
.mockResolvedValueOnce(false)
|
|
152
|
-
.mockResolvedValueOnce(true)
|
|
153
|
-
.mockResolvedValueOnce(false)
|
|
154
|
-
.mockResolvedValueOnce(true);
|
|
127
|
+
(input as jest.Mock).mockResolvedValueOnce(' ');
|
|
128
|
+
(confirm as jest.Mock).mockResolvedValue(true);
|
|
155
129
|
|
|
156
130
|
await getOptions(transformFiles);
|
|
157
131
|
|
|
@@ -159,7 +133,7 @@ describe('getOptions', () => {
|
|
|
159
133
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
160
134
|
const validateFn = (input as jest.Mock).mock.calls[0][0].validate!;
|
|
161
135
|
expect(validateFn(' ')).toBe('Target path cannot be empty');
|
|
162
|
-
expect(validateFn('./valid-path')).toBe(true);
|
|
136
|
+
expect(validateFn('./valid-path')).toBe(true); // Ensure valid input passes validation
|
|
163
137
|
expect(input).toHaveBeenCalledWith({
|
|
164
138
|
message: 'Enter the target directory or file path to run codemod on:',
|
|
165
139
|
validate: expect.any(Function),
|
|
@@ -168,12 +142,8 @@ describe('getOptions', () => {
|
|
|
168
142
|
|
|
169
143
|
it('should allow changing the default dry mode', async () => {
|
|
170
144
|
(list as jest.Mock).mockResolvedValue('fileC.tsx');
|
|
171
|
-
(input as jest.Mock).
|
|
172
|
-
(confirm as jest.Mock)
|
|
173
|
-
.mockResolvedValueOnce(false)
|
|
174
|
-
.mockResolvedValueOnce(false)
|
|
175
|
-
.mockResolvedValueOnce(false)
|
|
176
|
-
.mockResolvedValueOnce(true);
|
|
145
|
+
(input as jest.Mock).mockResolvedValue('./another-path');
|
|
146
|
+
(confirm as jest.Mock).mockResolvedValueOnce(false).mockResolvedValueOnce(false);
|
|
177
147
|
|
|
178
148
|
const options = await getOptions(transformFiles);
|
|
179
149
|
|
|
@@ -186,12 +156,8 @@ describe('getOptions', () => {
|
|
|
186
156
|
|
|
187
157
|
it('should allow changing the default print mode', async () => {
|
|
188
158
|
(list as jest.Mock).mockResolvedValue('fileA.js');
|
|
189
|
-
(input as jest.Mock).
|
|
190
|
-
(confirm as jest.Mock)
|
|
191
|
-
.mockResolvedValueOnce(false)
|
|
192
|
-
.mockResolvedValueOnce(true)
|
|
193
|
-
.mockResolvedValueOnce(true)
|
|
194
|
-
.mockResolvedValueOnce(true);
|
|
159
|
+
(input as jest.Mock).mockResolvedValue('./yet-another-path');
|
|
160
|
+
(confirm as jest.Mock).mockResolvedValueOnce(true).mockResolvedValueOnce(true);
|
|
195
161
|
|
|
196
162
|
const options = await getOptions(transformFiles);
|
|
197
163
|
|
|
@@ -201,19 +167,4 @@ describe('getOptions', () => {
|
|
|
201
167
|
});
|
|
202
168
|
expect(options.print).toBe(true);
|
|
203
169
|
});
|
|
204
|
-
|
|
205
|
-
it('should not prompt for monorepo when target path does not look like monorepo directory', async () => {
|
|
206
|
-
(list as jest.Mock).mockResolvedValue('fileA.js');
|
|
207
|
-
(input as jest.Mock).mockResolvedValueOnce('./src').mockResolvedValueOnce('');
|
|
208
|
-
(confirm as jest.Mock)
|
|
209
|
-
.mockResolvedValueOnce(false)
|
|
210
|
-
.mockResolvedValueOnce(true)
|
|
211
|
-
.mockResolvedValueOnce(false)
|
|
212
|
-
.mockResolvedValueOnce(true);
|
|
213
|
-
|
|
214
|
-
const options = await getOptions(transformFiles);
|
|
215
|
-
|
|
216
|
-
expect(confirm).toHaveBeenCalledTimes(4);
|
|
217
|
-
expect(options.isMonorepo).toBe(false);
|
|
218
|
-
});
|
|
219
170
|
});
|
package/src/utils/getOptions.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { confirm, input, select as list } from '@inquirer/prompts';
|
|
2
|
-
import path from 'path';
|
|
3
2
|
|
|
4
3
|
async function getOptions(transformFiles: string[]) {
|
|
5
4
|
const args = process.argv.slice(2);
|
|
@@ -14,7 +13,6 @@ async function getOptions(transformFiles: string[]) {
|
|
|
14
13
|
}
|
|
15
14
|
const gitignore = args.includes('--gitignore');
|
|
16
15
|
const noGitignore = args.includes('--no-gitignore');
|
|
17
|
-
const isMonorepo = args.includes('--monorepo');
|
|
18
16
|
|
|
19
17
|
if (!transformFile || !transformFiles.includes(transformFile)) {
|
|
20
18
|
throw new Error('Invalid transform file specified.');
|
|
@@ -26,15 +24,7 @@ async function getOptions(transformFiles: string[]) {
|
|
|
26
24
|
// If both --gitignore and --no-gitignore are specified, prioritize --gitignore
|
|
27
25
|
const useGitignore = !!(gitignore || (!gitignore && !noGitignore));
|
|
28
26
|
|
|
29
|
-
return {
|
|
30
|
-
transformFile,
|
|
31
|
-
targetPath,
|
|
32
|
-
dry,
|
|
33
|
-
print,
|
|
34
|
-
ignorePattern,
|
|
35
|
-
gitignore: useGitignore,
|
|
36
|
-
isMonorepo,
|
|
37
|
-
};
|
|
27
|
+
return { transformFile, targetPath, dry, print, ignorePattern, gitignore: useGitignore };
|
|
38
28
|
}
|
|
39
29
|
|
|
40
30
|
const transformFile = await list({
|
|
@@ -47,11 +37,6 @@ async function getOptions(transformFiles: string[]) {
|
|
|
47
37
|
validate: (value) => value.trim() !== '' || 'Target path cannot be empty',
|
|
48
38
|
});
|
|
49
39
|
|
|
50
|
-
const isMonorepo = await confirm({
|
|
51
|
-
message: 'Are you targeting a monorepo packages/apps directory?',
|
|
52
|
-
default: true,
|
|
53
|
-
});
|
|
54
|
-
|
|
55
40
|
const dry = await confirm({
|
|
56
41
|
message: 'Run in dry mode (no changes written to files)?',
|
|
57
42
|
default: true,
|
|
@@ -72,7 +57,7 @@ async function getOptions(transformFiles: string[]) {
|
|
|
72
57
|
default: true,
|
|
73
58
|
});
|
|
74
59
|
|
|
75
|
-
return { transformFile, targetPath, dry, print, ignorePattern, gitignore
|
|
60
|
+
return { transformFile, targetPath, dry, print, ignorePattern, gitignore };
|
|
76
61
|
}
|
|
77
62
|
|
|
78
63
|
export default getOptions;
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import type { Options } from 'jscodeshift';
|
|
2
|
-
|
|
3
|
-
import { validatePackageRequirements } from '../packageValidation';
|
|
4
|
-
|
|
5
|
-
describe('validatePackageRequirements', () => {
|
|
6
|
-
const requirements = [{ name: '@transferwise/components', version: '>=46.5.0' }];
|
|
7
|
-
|
|
8
|
-
beforeEach(() => {
|
|
9
|
-
jest.spyOn(console, 'debug').mockImplementation(() => {});
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
afterEach(() => {
|
|
13
|
-
jest.restoreAllMocks();
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
it('returns true when package is found (object format)', () => {
|
|
17
|
-
const options: Options = {
|
|
18
|
-
packageResults: { '@transferwise/components@>=46.5.0': true },
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
expect(validatePackageRequirements(options, requirements)).toBe(true);
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
it('returns true when package is found (JSON string format)', () => {
|
|
25
|
-
const options: Options = {
|
|
26
|
-
packageResults: '{"@transferwise/components@>=46.5.0":true}',
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
expect(validatePackageRequirements(options, requirements)).toBe(true);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it('returns false when package is missing', () => {
|
|
33
|
-
const options: Options = {
|
|
34
|
-
packageResults: { '@transferwise/components@>=46.5.0': false },
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
expect(validatePackageRequirements(options, requirements)).toBe(false);
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it('returns false when no packageResults provided', () => {
|
|
41
|
-
const options: Options = {};
|
|
42
|
-
|
|
43
|
-
expect(validatePackageRequirements(options, requirements)).toBe(false);
|
|
44
|
-
});
|
|
45
|
-
});
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import type { Options } from 'jscodeshift';
|
|
2
|
-
|
|
3
|
-
interface PackageRequirement {
|
|
4
|
-
name: string;
|
|
5
|
-
version: string;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Check if package requirements are met
|
|
10
|
-
* Returns true if all packages are found, false otherwise
|
|
11
|
-
*/
|
|
12
|
-
export function validatePackageRequirements(
|
|
13
|
-
options: Options,
|
|
14
|
-
requirements: PackageRequirement[],
|
|
15
|
-
): boolean {
|
|
16
|
-
let packageResults: Record<string, boolean> = {};
|
|
17
|
-
|
|
18
|
-
// Handle both string and object cases
|
|
19
|
-
if (options.packageResults) {
|
|
20
|
-
if (typeof options.packageResults === 'string') {
|
|
21
|
-
// Parse JSON string
|
|
22
|
-
try {
|
|
23
|
-
if (options.packageResults.trim() && options.packageResults.trim().startsWith('{')) {
|
|
24
|
-
packageResults = JSON.parse(options.packageResults) as Record<string, boolean>;
|
|
25
|
-
}
|
|
26
|
-
} catch (error) {
|
|
27
|
-
console.debug('Failed to parse packageResults:', error);
|
|
28
|
-
return false;
|
|
29
|
-
}
|
|
30
|
-
} else if (typeof options.packageResults === 'object') {
|
|
31
|
-
// Already an object, use directly
|
|
32
|
-
packageResults = options.packageResults as Record<string, boolean>;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// Check if all requirements are met
|
|
37
|
-
const allRequirementsMet = requirements.every((req) => {
|
|
38
|
-
const key = `${req.name}@${req.version}`;
|
|
39
|
-
return packageResults[key];
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
if (!allRequirementsMet) {
|
|
43
|
-
const missing = requirements.filter((req) => {
|
|
44
|
-
const key = `${req.name}@${req.version}`;
|
|
45
|
-
return !packageResults[key];
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
const missingPackages = missing.map((pkg) => `${pkg.name}@${pkg.version}`).join(', ');
|
|
49
|
-
console.debug(`Skipping transform - missing required packages: ${missingPackages}`);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return allRequirementsMet;
|
|
53
|
-
}
|
|
@@ -1,190 +0,0 @@
|
|
|
1
|
-
import { existsSync, readdirSync, readFileSync, type Stats, statSync } from 'node:fs';
|
|
2
|
-
|
|
3
|
-
import hasPackageVersion, { packageVersionCache } from '../hasPackageVersion';
|
|
4
|
-
|
|
5
|
-
jest.mock('node:fs');
|
|
6
|
-
|
|
7
|
-
const mockExistsSync = existsSync as jest.MockedFunction<typeof existsSync>;
|
|
8
|
-
const mockReadFileSync = readFileSync as jest.MockedFunction<typeof readFileSync>;
|
|
9
|
-
const mockReaddirSync = readdirSync as jest.MockedFunction<typeof readdirSync>;
|
|
10
|
-
const mockStatSync = statSync as jest.MockedFunction<typeof statSync>;
|
|
11
|
-
const consoleSpy = jest.spyOn(console, 'log').mockImplementation(() => {});
|
|
12
|
-
|
|
13
|
-
describe('hasPackageVersion', () => {
|
|
14
|
-
beforeEach(() => {
|
|
15
|
-
jest.clearAllMocks();
|
|
16
|
-
mockExistsSync.mockReturnValue(false);
|
|
17
|
-
|
|
18
|
-
const mockStats: Partial<Stats> = {
|
|
19
|
-
isFile: jest.fn(() => false),
|
|
20
|
-
isDirectory: jest.fn(() => true),
|
|
21
|
-
isBlockDevice: jest.fn(() => false),
|
|
22
|
-
isCharacterDevice: jest.fn(() => false),
|
|
23
|
-
isFIFO: jest.fn(() => false),
|
|
24
|
-
isSocket: jest.fn(() => false),
|
|
25
|
-
isSymbolicLink: jest.fn(() => false),
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
mockStatSync.mockReturnValue(mockStats as Stats);
|
|
29
|
-
packageVersionCache.clear();
|
|
30
|
-
consoleSpy.mockClear();
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
afterAll(() => {
|
|
34
|
-
consoleSpy.mockRestore();
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it('finds package in package.json dependencies', () => {
|
|
38
|
-
mockExistsSync.mockImplementation((path) => {
|
|
39
|
-
const pathStr = String(path);
|
|
40
|
-
return pathStr.endsWith('package.json') && !pathStr.includes('node_modules');
|
|
41
|
-
});
|
|
42
|
-
mockReadFileSync.mockReturnValue(
|
|
43
|
-
JSON.stringify({
|
|
44
|
-
dependencies: {
|
|
45
|
-
'@transferwise/components': '46.5.0',
|
|
46
|
-
},
|
|
47
|
-
}),
|
|
48
|
-
);
|
|
49
|
-
|
|
50
|
-
expect(hasPackageVersion('@transferwise/components', '>=46.5.0')).toBe(true);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it('finds package in standard node_modules directory', () => {
|
|
54
|
-
mockExistsSync.mockImplementation((path) => {
|
|
55
|
-
const pathStr = String(path);
|
|
56
|
-
return (
|
|
57
|
-
pathStr.includes('node_modules/@transferwise/components/package.json') &&
|
|
58
|
-
!pathStr.includes('.pnpm')
|
|
59
|
-
);
|
|
60
|
-
});
|
|
61
|
-
mockReadFileSync.mockReturnValue(
|
|
62
|
-
JSON.stringify({
|
|
63
|
-
version: '46.5.0',
|
|
64
|
-
}),
|
|
65
|
-
);
|
|
66
|
-
|
|
67
|
-
expect(hasPackageVersion('@transferwise/components', '>=46.5.0')).toBe(true);
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it('finds package in pnpm node_modules structure', () => {
|
|
71
|
-
mockExistsSync.mockImplementation((path) => {
|
|
72
|
-
const pathStr = String(path);
|
|
73
|
-
return (
|
|
74
|
-
pathStr.includes('node_modules/.pnpm') ||
|
|
75
|
-
pathStr.includes(
|
|
76
|
-
'@transferwise+components@46.5.0/node_modules/@transferwise/components/package.json',
|
|
77
|
-
)
|
|
78
|
-
);
|
|
79
|
-
});
|
|
80
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
81
|
-
mockReaddirSync.mockReturnValue([
|
|
82
|
-
'@transferwise+components@46.5.0',
|
|
83
|
-
'other-package@1.0.0',
|
|
84
|
-
] as any);
|
|
85
|
-
mockReadFileSync.mockReturnValue(
|
|
86
|
-
JSON.stringify({
|
|
87
|
-
version: '46.5.0',
|
|
88
|
-
}),
|
|
89
|
-
);
|
|
90
|
-
|
|
91
|
-
expect(hasPackageVersion('@transferwise/components', '>=46.5.0')).toBe(true);
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
it('returns false when package is not found', () => {
|
|
95
|
-
mockExistsSync.mockReturnValue(false);
|
|
96
|
-
|
|
97
|
-
expect(hasPackageVersion('@nonexistent/package', '>=1.0.0')).toBe(false);
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
it('returns false when version does not satisfy requirement', () => {
|
|
101
|
-
mockExistsSync.mockImplementation((path) => {
|
|
102
|
-
const pathStr = String(path);
|
|
103
|
-
return pathStr.endsWith('package.json') && !pathStr.includes('node_modules');
|
|
104
|
-
});
|
|
105
|
-
mockReadFileSync.mockReturnValue(
|
|
106
|
-
JSON.stringify({
|
|
107
|
-
dependencies: {
|
|
108
|
-
'@transferwise/components': '40.0.0',
|
|
109
|
-
},
|
|
110
|
-
}),
|
|
111
|
-
);
|
|
112
|
-
|
|
113
|
-
expect(hasPackageVersion('@transferwise/components', '>=46.5.0')).toBe(false);
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
it('handles malformed JSON gracefully', () => {
|
|
117
|
-
mockExistsSync.mockImplementation((path) => {
|
|
118
|
-
const pathStr = String(path);
|
|
119
|
-
return pathStr.endsWith('package.json') && !pathStr.includes('node_modules');
|
|
120
|
-
});
|
|
121
|
-
mockReadFileSync.mockReturnValue('invalid json');
|
|
122
|
-
|
|
123
|
-
expect(hasPackageVersion('@transferwise/components', '>=46.5.0')).toBe(false);
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
it('finds package in devDependencies', () => {
|
|
127
|
-
mockExistsSync.mockImplementation((path) => {
|
|
128
|
-
const pathStr = String(path);
|
|
129
|
-
return pathStr.endsWith('package.json') && !pathStr.includes('node_modules');
|
|
130
|
-
});
|
|
131
|
-
mockReadFileSync.mockReturnValue(
|
|
132
|
-
JSON.stringify({
|
|
133
|
-
devDependencies: {
|
|
134
|
-
'@transferwise/components': '46.5.0',
|
|
135
|
-
},
|
|
136
|
-
}),
|
|
137
|
-
);
|
|
138
|
-
|
|
139
|
-
expect(hasPackageVersion('@transferwise/components', '>=46.5.0')).toBe(true);
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
it('finds package in peerDependencies', () => {
|
|
143
|
-
mockExistsSync.mockImplementation((path) => {
|
|
144
|
-
const pathStr = String(path);
|
|
145
|
-
return pathStr.endsWith('package.json') && !pathStr.includes('node_modules');
|
|
146
|
-
});
|
|
147
|
-
mockReadFileSync.mockReturnValue(
|
|
148
|
-
JSON.stringify({
|
|
149
|
-
peerDependencies: {
|
|
150
|
-
'@transferwise/components': '>=46.5.0',
|
|
151
|
-
},
|
|
152
|
-
}),
|
|
153
|
-
);
|
|
154
|
-
|
|
155
|
-
expect(hasPackageVersion('@transferwise/components', '>=46.5.0')).toBe(true);
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
it('handles complex version ranges', () => {
|
|
159
|
-
mockExistsSync.mockImplementation((path) => {
|
|
160
|
-
const pathStr = String(path);
|
|
161
|
-
return pathStr.endsWith('package.json') && !pathStr.includes('node_modules');
|
|
162
|
-
});
|
|
163
|
-
mockReadFileSync.mockReturnValue(
|
|
164
|
-
JSON.stringify({
|
|
165
|
-
dependencies: {
|
|
166
|
-
'@transferwise/components': '^46.5.0',
|
|
167
|
-
},
|
|
168
|
-
}),
|
|
169
|
-
);
|
|
170
|
-
|
|
171
|
-
expect(hasPackageVersion('@transferwise/components', '>=46.5.0')).toBe(true);
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
it('uses cache for repeated calls', () => {
|
|
175
|
-
mockExistsSync.mockImplementation((path) => {
|
|
176
|
-
const pathStr = String(path);
|
|
177
|
-
return pathStr.endsWith('package.json') && !pathStr.includes('node_modules');
|
|
178
|
-
});
|
|
179
|
-
mockReadFileSync.mockReturnValue(
|
|
180
|
-
JSON.stringify({
|
|
181
|
-
dependencies: {
|
|
182
|
-
'@transferwise/components': '46.5.0',
|
|
183
|
-
},
|
|
184
|
-
}),
|
|
185
|
-
);
|
|
186
|
-
|
|
187
|
-
expect(hasPackageVersion('@transferwise/components', '>=46.5.0')).toBe(true);
|
|
188
|
-
expect(mockReadFileSync).toHaveBeenCalledTimes(1);
|
|
189
|
-
});
|
|
190
|
-
});
|