ts-codemod-lib 1.0.1 → 1.1.0

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 (49) hide show
  1. package/README.md +428 -6
  2. package/dist/cmd/append-as-const.d.mts +3 -0
  3. package/dist/cmd/append-as-const.d.mts.map +1 -0
  4. package/dist/cmd/append-as-const.mjs +138 -0
  5. package/dist/cmd/append-as-const.mjs.map +1 -0
  6. package/dist/cmd/convert-interface-to-type.d.mts +3 -0
  7. package/dist/cmd/convert-interface-to-type.d.mts.map +1 -0
  8. package/dist/cmd/convert-interface-to-type.mjs +138 -0
  9. package/dist/cmd/convert-interface-to-type.mjs.map +1 -0
  10. package/dist/cmd/convert-to-readonly.mjs +3 -2
  11. package/dist/cmd/convert-to-readonly.mjs.map +1 -1
  12. package/dist/cmd/replace-any-with-unknown.d.mts +3 -0
  13. package/dist/cmd/replace-any-with-unknown.d.mts.map +1 -0
  14. package/dist/cmd/replace-any-with-unknown.mjs +138 -0
  15. package/dist/cmd/replace-any-with-unknown.mjs.map +1 -0
  16. package/dist/cmd/replace-record-with-unknown-record.d.mts +3 -0
  17. package/dist/cmd/replace-record-with-unknown-record.d.mts.map +1 -0
  18. package/dist/cmd/replace-record-with-unknown-record.mjs +138 -0
  19. package/dist/cmd/replace-record-with-unknown-record.mjs.map +1 -0
  20. package/dist/entry-point.mjs +2 -0
  21. package/dist/entry-point.mjs.map +1 -1
  22. package/dist/functions/ast-transformers/append-as-const.d.mts +13 -0
  23. package/dist/functions/ast-transformers/append-as-const.d.mts.map +1 -0
  24. package/dist/functions/ast-transformers/append-as-const.mjs +98 -0
  25. package/dist/functions/ast-transformers/append-as-const.mjs.map +1 -0
  26. package/dist/functions/ast-transformers/index.d.mts +2 -0
  27. package/dist/functions/ast-transformers/index.d.mts.map +1 -1
  28. package/dist/functions/ast-transformers/index.mjs +2 -0
  29. package/dist/functions/ast-transformers/index.mjs.map +1 -1
  30. package/dist/functions/ast-transformers/replace-any-with-unknown.d.mts +3 -0
  31. package/dist/functions/ast-transformers/replace-any-with-unknown.d.mts.map +1 -0
  32. package/dist/functions/ast-transformers/replace-any-with-unknown.mjs +38 -0
  33. package/dist/functions/ast-transformers/replace-any-with-unknown.mjs.map +1 -0
  34. package/dist/functions/index.mjs +2 -0
  35. package/dist/functions/index.mjs.map +1 -1
  36. package/dist/index.mjs +2 -0
  37. package/dist/index.mjs.map +1 -1
  38. package/package.json +60 -56
  39. package/src/cmd/append-as-const.mts +197 -0
  40. package/src/cmd/convert-interface-to-type.mts +197 -0
  41. package/src/cmd/convert-to-readonly.mts +3 -1
  42. package/src/cmd/replace-any-with-unknown.mts +197 -0
  43. package/src/cmd/replace-record-with-unknown-record.mts +197 -0
  44. package/src/functions/ast-transformers/append-as-const.mts +154 -0
  45. package/src/functions/ast-transformers/append-as-const.test.mts +339 -0
  46. package/src/functions/ast-transformers/convert-to-readonly-type.test.mts +3 -3
  47. package/src/functions/ast-transformers/index.mts +2 -0
  48. package/src/functions/ast-transformers/replace-any-with-unknown.mts +49 -0
  49. package/src/functions/ast-transformers/replace-any-with-unknown.test.mts +140 -0
@@ -0,0 +1,339 @@
1
+ /* eslint-disable import-x/namespace */
2
+ /* eslint-disable tree-shakable/import-star */
3
+ /* eslint-disable vitest/expect-expect */
4
+ import dedent from 'dedent';
5
+ import * as prettierPluginEstree from 'prettier/plugins/estree';
6
+ import * as prettierPluginTypeScript from 'prettier/plugins/typescript';
7
+ import * as prettier from 'prettier/standalone';
8
+ import { appendAsConstTransformer } from './append-as-const.mjs';
9
+ import { transformSourceCode } from './transform-source-code.mjs';
10
+
11
+ const testFn = async ({
12
+ source,
13
+ expected,
14
+ debug,
15
+ }: Readonly<{
16
+ source: string;
17
+ expected: string;
18
+ debug?: boolean;
19
+ }>): Promise<void> => {
20
+ if (debug !== true) {
21
+ // eslint-disable-next-line vitest/no-restricted-vi-methods
22
+ vi.spyOn(console, 'debug').mockImplementation(() => {});
23
+
24
+ // eslint-disable-next-line vitest/no-restricted-vi-methods
25
+ vi.spyOn(console, 'log').mockImplementation(() => {});
26
+ }
27
+
28
+ const transformed = await formatter(
29
+ transformSourceCode(source, false, [appendAsConstTransformer()]),
30
+ );
31
+
32
+ const expectedFormatted = await formatter(expected);
33
+
34
+ expect(transformed).toBe(expectedFormatted);
35
+ };
36
+
37
+ const formatter = (code: string): Promise<string> =>
38
+ prettier.format(code, {
39
+ parser: 'typescript',
40
+ plugins: [prettierPluginTypeScript, prettierPluginEstree],
41
+ });
42
+
43
+ describe(appendAsConstTransformer, () => {
44
+ test.each([
45
+ {
46
+ name: 'ArrayLiteralExpression - simple',
47
+ source: 'const foo = [1, 2, 3];',
48
+ expected: 'const foo = [1, 2, 3] as const;',
49
+ },
50
+ {
51
+ name: 'ArrayLiteralExpression - empty',
52
+ source: 'const emptyArray = [];',
53
+ expected: 'const emptyArray = [] as const;',
54
+ },
55
+ {
56
+ name: 'ArrayLiteralExpression - nested',
57
+ source: 'const nested = [1, [2, 3], 4];',
58
+ expected: 'const nested = [1, [2, 3], 4] as const;',
59
+ },
60
+ {
61
+ name: 'ArrayLiteralExpression - with strings',
62
+ source: "const strArray = ['a', 'b', 'c'];",
63
+ expected: "const strArray = ['a', 'b', 'c'] as const;",
64
+ },
65
+ {
66
+ name: 'ArrayLiteralExpression - mixed types',
67
+ source: "const mixed = [1, 'a', true, null];",
68
+ expected: "const mixed = [1, 'a', true, null] as const;",
69
+ },
70
+ {
71
+ name: 'ObjectLiteralExpression - simple',
72
+ source: 'const obj = { a: 1, b: 2 };',
73
+ expected: 'const obj = { a: 1, b: 2 } as const;',
74
+ },
75
+ {
76
+ name: 'ObjectLiteralExpression - empty',
77
+ source: 'const emptyObj = {};',
78
+ expected: 'const emptyObj = {} as const;',
79
+ },
80
+ {
81
+ name: 'ObjectLiteralExpression - nested',
82
+ source: 'const nestedObj = { a: 1, b: { c: 2 } };',
83
+ expected: 'const nestedObj = { a: 1, b: { c: 2 } } as const;',
84
+ },
85
+ {
86
+ name: 'ObjectLiteralExpression - with array',
87
+ source: 'const objWithArray = { a: 1, b: [1, 2, 3] };',
88
+ expected: 'const objWithArray = { a: 1, b: [1, 2, 3] } as const;',
89
+ },
90
+ {
91
+ name: 'Mixed - array with object',
92
+ source: 'const arrayWithObj = [{ a: 1 }, { b: 2 }];',
93
+ expected: 'const arrayWithObj = [{ a: 1 }, { b: 2 }] as const;',
94
+ },
95
+ {
96
+ name: 'Variable declaration with multiple variables',
97
+ source: 'const a = [1, 2], b = { c: 3 };',
98
+ expected: 'const a = [1, 2] as const, b = { c: 3 } as const;',
99
+ },
100
+ {
101
+ name: 'Variable declaration with mut_ keyword',
102
+ source: 'const mut_foo = [1, 2, 3];',
103
+ expected: 'const mut_foo = [1, 2, 3];',
104
+ },
105
+ {
106
+ name: 'Variable declaration with multiple variables',
107
+ source: 'const mut_a = [1, 2], mut_b = { c: 3 };',
108
+ expected: 'const mut_a = [1, 2], mut_b = { c: 3 };',
109
+ },
110
+ {
111
+ name: 'Function return',
112
+ source: 'function foo() { return [1, 2, 3]; }',
113
+ expected: 'function foo() { return [1, 2, 3] as const; }',
114
+ },
115
+ {
116
+ name: 'Arrow function return',
117
+ source: 'const foo = () => ({ a: 1, b: 2 });',
118
+ expected: 'const foo = () => ({ a: 1, b: 2 } as const);',
119
+ },
120
+ {
121
+ name: 'Array in function parameter',
122
+ source: 'function foo(a = [1, 2]) { return a; }',
123
+ expected: 'function foo(a = [1, 2] as const) { return a; }',
124
+ },
125
+ {
126
+ name: 'Object in function parameter',
127
+ source: 'function foo(a = { b: 1 }) { return a; }',
128
+ expected: 'function foo(a = { b: 1 } as const) { return a; }',
129
+ },
130
+ {
131
+ name: 'Object in function call',
132
+ source: 'const a = foo({ b: 1 });',
133
+ expected: 'const a = foo({ b: 1 });',
134
+ },
135
+ {
136
+ name: 'Skip ArrayLiteralExpression with disable comment',
137
+ source: dedent`
138
+ // transformer-ignore-next-line
139
+ const skippedArray = [1, 2, 3];
140
+ `,
141
+ expected: dedent`
142
+ // transformer-ignore-next-line
143
+ const skippedArray = [1, 2, 3];
144
+ `,
145
+ },
146
+ {
147
+ name: 'Skip ObjectLiteralExpression with disable comment',
148
+ source: dedent`
149
+ // transformer-ignore-next-line
150
+ const skippedObject = { a: 1, b: "hello" };
151
+ `,
152
+ expected: dedent`
153
+ // transformer-ignore-next-line
154
+ const skippedObject = { a: 1, b: "hello" };
155
+ `,
156
+ },
157
+ {
158
+ name: 'Disable comment only affects the immediate next line',
159
+ source: dedent`
160
+ const transformedArray = [10, 20]; // This should be transformed
161
+ // transformer-ignore-next-line
162
+ const skippedObject = { x: true }; // This should be skipped
163
+ const transformedObject = { y: false }; // This should be transformed
164
+ `,
165
+ expected: dedent`
166
+ const transformedArray = [10, 20] as const; // This should be transformed
167
+ // transformer-ignore-next-line
168
+ const skippedObject = { x: true }; // This should be skipped
169
+ const transformedObject = { y: false } as const; // This should be transformed
170
+ `,
171
+ },
172
+ {
173
+ name: 'File scope transformer-ignore',
174
+ source: dedent`
175
+ /* transformer-ignore */
176
+ const transformedArray = [10, 20]; // This should be skipped
177
+ const skippedObject = { x: true }; // This should be skipped
178
+ const transformedObject = { y: false }; // This should be skipped
179
+ `,
180
+ expected: dedent`
181
+ /* transformer-ignore */
182
+ const transformedArray = [10, 20]; // This should be skipped
183
+ const skippedObject = { x: true }; // This should be skipped
184
+ const transformedObject = { y: false }; // This should be skipped
185
+ `,
186
+ },
187
+ // Cases where the transformer doesn't modify the code
188
+ {
189
+ name: 'Primitive literal - number',
190
+ source: 'const num = 42;',
191
+ expected: 'const num = 42;',
192
+ },
193
+ {
194
+ name: 'Primitive literal - string',
195
+ source: "const str = 'hello';",
196
+ expected: "const str = 'hello';",
197
+ },
198
+ {
199
+ name: 'Primitive literal - boolean',
200
+ source: 'const bool = true;',
201
+ expected: 'const bool = true;',
202
+ },
203
+ {
204
+ name: 'Primitive literal - null',
205
+ source: 'const n = null;',
206
+ expected: 'const n = null;',
207
+ },
208
+ {
209
+ name: 'Primitive literal - undefined',
210
+ source: 'const u = undefined;',
211
+ expected: 'const u = undefined;',
212
+ },
213
+ {
214
+ name: 'Variable reference',
215
+ source: 'const a = 1; const b = a;',
216
+ expected: 'const a = 1; const b = a;',
217
+ },
218
+ {
219
+ name: 'Function call',
220
+ source: 'const result = foo();',
221
+ expected: 'const result = foo();',
222
+ },
223
+ {
224
+ name: 'Method call',
225
+ source: 'const result = obj.method();',
226
+ expected: 'const result = obj.method();',
227
+ },
228
+ {
229
+ name: 'Binary expression',
230
+ source: 'const sum = a + b;',
231
+ expected: 'const sum = a + b;',
232
+ },
233
+ {
234
+ name: 'Template literal',
235
+ // eslint-disable-next-line no-template-curly-in-string
236
+ source: 'const greeting = `Hello ${name}`;',
237
+ // eslint-disable-next-line no-template-curly-in-string
238
+ expected: 'const greeting = `Hello ${name}`;',
239
+ },
240
+ {
241
+ name: 'New expression',
242
+ source: 'const date = new Date();',
243
+ expected: 'const date = new Date();',
244
+ },
245
+ {
246
+ name: 'Already has as const - array',
247
+ source: 'const arr = [1, 2, 3] as const;',
248
+ expected: 'const arr = [1, 2, 3] as const;',
249
+ },
250
+ {
251
+ name: 'Already has as const - object',
252
+ source: 'const obj = { a: 1 } as const;',
253
+ expected: 'const obj = { a: 1 } as const;',
254
+ },
255
+ {
256
+ name: 'Primitive literal - number',
257
+ source: 'const num = 42;',
258
+ expected: 'const num = 42;',
259
+ },
260
+ {
261
+ name: 'Class declaration',
262
+ source: 'class MyClass { prop = 1; }',
263
+ expected: 'class MyClass { prop = 1; }',
264
+ },
265
+
266
+ // Tests for removing nested as const
267
+ {
268
+ name: 'should remove nested as const from array literal',
269
+ source: 'const arr = [[1, 2] as const, [3, 4]] as const;',
270
+ expected: 'const arr = [[1, 2], [3, 4]] as const;',
271
+ },
272
+ {
273
+ name: 'should remove nested as const from object literal',
274
+ source: 'const obj = { a: { b: 1 } as const, c: 2 } as const;',
275
+ expected: 'const obj = { a: { b: 1 }, c: 2 } as const;',
276
+ },
277
+ {
278
+ name: 'should remove nested as const from mixed literals',
279
+ source: 'const mixed = [{ a: 1 } as const, [2] as const] as const;',
280
+ expected: 'const mixed = [{ a: 1 }, [2]] as const;',
281
+ },
282
+ {
283
+ name: 'should remove deeply nested as const',
284
+ source: 'const deep = [[{ x: 1 as const }] as const] as const;',
285
+ expected: 'const deep = [[{ x: 1 }]] as const;',
286
+ },
287
+
288
+ // Tests for removing as const from primitives within literals
289
+ {
290
+ name: 'should remove as const from number literal inside array and add as const to top level',
291
+ source: 'const arr = [1 as const, 2];',
292
+ expected: 'const arr = [1, 2] as const;',
293
+ },
294
+ {
295
+ name: 'should remove as const from string literal inside object and add as const to top level',
296
+ source: 'const obj = { key: "value" as const };',
297
+ expected: 'const obj = { key: "value" } as const;',
298
+ },
299
+ {
300
+ name: 'should remove as const from boolean literal inside array and add as const to top level',
301
+ source: 'const arr = [true as const, false];',
302
+ expected: 'const arr = [true, false] as const;',
303
+ },
304
+ {
305
+ name: 'should remove multiple primitive as const inside literals and add as const to top level',
306
+ source:
307
+ 'const mixed = [1 as const, { val: "str" as const }, false as const];',
308
+ expected: 'const mixed = [1, { val: "str" }, false] as const;',
309
+ },
310
+ {
311
+ name: 'should remove primitive as const even if parent already has as const',
312
+ source: 'const arr = [1 as const, 2] as const;',
313
+ expected: 'const arr = [1, 2] as const;',
314
+ },
315
+ {
316
+ name: 'should remove primitive as const in nested object and add as const to top level',
317
+ source: 'const obj = { data: { value: 123 as const } };',
318
+ expected: 'const obj = { data: { value: 123 } } as const;',
319
+ },
320
+
321
+ {
322
+ name: 'arrow function',
323
+ source: dedent`
324
+ export const some = <const S,>(value: S): Some<S> =>
325
+ ({
326
+ type: SomeTypeSymbol,
327
+ value
328
+ }) as const;
329
+ `,
330
+ expected: dedent`
331
+ export const some = <const S,>(value: S): Some<S> =>
332
+ ({
333
+ type: SomeTypeSymbol,
334
+ value
335
+ }) as const;
336
+ `,
337
+ },
338
+ ])('$name', testFn);
339
+ });
@@ -2,8 +2,8 @@
2
2
  /* eslint-disable tree-shakable/import-star */
3
3
  /* eslint-disable vitest/expect-expect */
4
4
  import dedent from 'dedent';
5
- import * as parserTypeScript from 'prettier/parser-typescript';
6
5
  import * as prettierPluginEstree from 'prettier/plugins/estree';
6
+ import * as prettierPluginTypeScript from 'prettier/plugins/typescript';
7
7
  import * as prettier from 'prettier/standalone';
8
8
  import {
9
9
  convertToReadonlyTypeTransformer,
@@ -77,14 +77,14 @@ const normalizeWhitespaceForComparison = (code: string): string => {
77
77
  const formatter = async (code: string): Promise<string> => {
78
78
  const formatOnce = await prettier.format(code, {
79
79
  parser: 'typescript',
80
- plugins: [parserTypeScript, prettierPluginEstree],
80
+ plugins: [prettierPluginTypeScript, prettierPluginEstree],
81
81
  });
82
82
 
83
83
  const whitespaceNormalized = normalizeWhitespaceForComparison(formatOnce);
84
84
 
85
85
  return prettier.format(whitespaceNormalized, {
86
86
  parser: 'typescript',
87
- plugins: [parserTypeScript, prettierPluginEstree],
87
+ plugins: [prettierPluginTypeScript, prettierPluginEstree],
88
88
  });
89
89
  };
90
90
 
@@ -1,6 +1,8 @@
1
+ export * from './append-as-const.mjs';
1
2
  export * from './convert-interface-to-type.mjs';
2
3
  export * from './convert-to-readonly-type.mjs';
3
4
  export * from './readonly-transformer-helpers/index.mjs';
5
+ export * from './replace-any-with-unknown.mjs';
4
6
  export * from './replace-record-with-unknown-record.mjs';
5
7
  export * from './transform-source-code.mjs';
6
8
  export * from './types.mjs';
@@ -0,0 +1,49 @@
1
+ import * as tsm from 'ts-morph';
2
+ import {
3
+ hasDisableNextLineComment,
4
+ isSpreadNamedTupleMemberNode,
5
+ isSpreadParameterNode,
6
+ } from '../functions/index.mjs';
7
+ import { type TsMorphTransformer } from './types.mjs';
8
+
9
+ export const replaceAnyWithUnknownTransformer = (): TsMorphTransformer => body;
10
+
11
+ const body: TsMorphTransformer = (sourceAst) => {
12
+ for (const node of sourceAst.getChildren()) {
13
+ transformNode(node);
14
+ }
15
+ };
16
+
17
+ const transformNode = (node: tsm.Node): void => {
18
+ if (hasDisableNextLineComment(node)) {
19
+ console.debug('skipped by disable-next-line comment');
20
+
21
+ return;
22
+ }
23
+
24
+ if (node.isKind(tsm.SyntaxKind.AnyKeyword)) {
25
+ const anyKeywordNode = node;
26
+
27
+ const parent = anyKeywordNode.getParent();
28
+
29
+ if (
30
+ parent !== undefined &&
31
+ // `(...args: any) => any` -> `(...args: unknown[]) => any`
32
+ (isSpreadParameterNode(parent) ||
33
+ // `[name: E0, ...args: any)]` -> `[name: E0, ...args: unknown[]]`
34
+ isSpreadNamedTupleMemberNode(parent))
35
+ ) {
36
+ anyKeywordNode.replaceWithText('readonly unknown[]');
37
+
38
+ return;
39
+ }
40
+
41
+ anyKeywordNode.replaceWithText('unknown');
42
+
43
+ return;
44
+ }
45
+
46
+ for (const child of node.getChildren()) {
47
+ transformNode(child);
48
+ }
49
+ };
@@ -0,0 +1,140 @@
1
+ /* eslint-disable import-x/namespace */
2
+ /* eslint-disable tree-shakable/import-star */
3
+ /* eslint-disable vitest/expect-expect */
4
+ import dedent from 'dedent';
5
+ import * as prettierPluginEstree from 'prettier/plugins/estree';
6
+ import * as prettierPluginTypeScript from 'prettier/plugins/typescript';
7
+ import * as prettier from 'prettier/standalone';
8
+ import { replaceAnyWithUnknownTransformer } from './replace-any-with-unknown.mjs';
9
+ import { transformSourceCode } from './transform-source-code.mjs';
10
+
11
+ const testFn = async ({
12
+ source,
13
+ expected,
14
+ debug,
15
+ }: Readonly<{
16
+ source: string;
17
+ expected: string;
18
+ debug?: boolean;
19
+ }>): Promise<void> => {
20
+ if (debug !== true) {
21
+ // eslint-disable-next-line vitest/no-restricted-vi-methods
22
+ vi.spyOn(console, 'debug').mockImplementation(() => {});
23
+
24
+ // eslint-disable-next-line vitest/no-restricted-vi-methods
25
+ vi.spyOn(console, 'log').mockImplementation(() => {});
26
+ }
27
+
28
+ const transformed = await formatter(
29
+ transformSourceCode(source, false, [replaceAnyWithUnknownTransformer()]),
30
+ );
31
+
32
+ const expectedFormatted = await formatter(expected);
33
+
34
+ expect(transformed).toBe(expectedFormatted);
35
+ };
36
+
37
+ const formatter = (code: string): Promise<string> =>
38
+ prettier.format(code, {
39
+ parser: 'typescript',
40
+ plugins: [prettierPluginTypeScript, prettierPluginEstree],
41
+ });
42
+
43
+ describe(replaceAnyWithUnknownTransformer, () => {
44
+ test.each([
45
+ {
46
+ name: 'Simple type alias',
47
+ source: 'type Foo = any',
48
+ expected: 'type Foo = unknown',
49
+ },
50
+ {
51
+ name: 'Readonly array',
52
+ source: 'type Foo = readonly any[]',
53
+ expected: 'type Foo = readonly unknown[]',
54
+ },
55
+ {
56
+ name: 'Spread syntax in function arguments',
57
+ source: 'const fn = (...args: any): void => {}',
58
+ expected: 'const fn = (...args: readonly unknown[]): void => {}',
59
+ },
60
+ {
61
+ name: 'Spread syntax in named tuple member',
62
+ source: 'type Foo = [number, ...args: any];',
63
+ expected: 'type Foo = [number, ...args: readonly unknown[]];',
64
+ },
65
+ {
66
+ name: 'Skip simple type alias with disable comment',
67
+ source: dedent`
68
+ // transformer-ignore-next-line
69
+ type Foo = any;
70
+ `,
71
+ expected: dedent`
72
+ // transformer-ignore-next-line
73
+ type Foo = any;
74
+ `,
75
+ },
76
+ {
77
+ name: 'Skip readonly array with disable comment',
78
+ source: dedent`
79
+ // transformer-ignore-next-line
80
+ type Foo = readonly any[];
81
+ `,
82
+ expected: dedent`
83
+ // transformer-ignore-next-line
84
+ type Foo = readonly any[];
85
+ `,
86
+ },
87
+ {
88
+ name: 'Skip spread syntax in function args with disable comment',
89
+ source: dedent`
90
+ // transformer-ignore-next-line
91
+ const fn = (...args: any): void => {}
92
+ `,
93
+ expected: dedent`
94
+ // transformer-ignore-next-line
95
+ const fn = (...args: any): void => {}
96
+ `,
97
+ },
98
+ {
99
+ name: 'Skip spread syntax in tuple member with disable comment',
100
+ source: dedent`
101
+ // transformer-ignore-next-line
102
+ type Foo = [number, ...args: any];
103
+ `,
104
+ expected: dedent`
105
+ // transformer-ignore-next-line
106
+ type Foo = [number, ...args: any];
107
+ `,
108
+ },
109
+ {
110
+ name: 'Disable comment only affects next line (mixed types)',
111
+ source: dedent`
112
+ type A = any; // Should be unknown
113
+ // transformer-ignore-next-line
114
+ type B = any; // Should remain any
115
+ type C = any; // Should be unknown
116
+ `,
117
+ expected: dedent`
118
+ type A = unknown; // Should be unknown
119
+ // transformer-ignore-next-line
120
+ type B = any; // Should remain any
121
+ type C = unknown; // Should be unknown
122
+ `,
123
+ },
124
+ {
125
+ name: 'File scope transformer-ignore',
126
+ source: dedent`
127
+ /* transformer-ignore */
128
+ type A = any; // Should remain any
129
+ type B = any; // Should remain any
130
+ type C = any; // Should remain any
131
+ `,
132
+ expected: dedent`
133
+ /* transformer-ignore */
134
+ type A = any; // Should remain any
135
+ type B = any; // Should remain any
136
+ type C = any; // Should remain any
137
+ `,
138
+ },
139
+ ])('$name', testFn);
140
+ });