ts-codemod-lib 3.3.2 → 3.4.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 (26) hide show
  1. package/README.md +16 -16
  2. package/dist/functions/ast-transformers/append-as-const.mjs +11 -10
  3. package/dist/functions/ast-transformers/append-as-const.mjs.map +1 -1
  4. package/dist/functions/ast-transformers/convert-to-readonly.mjs +74 -1
  5. package/dist/functions/ast-transformers/convert-to-readonly.mjs.map +1 -1
  6. package/dist/functions/ast-transformers/replace-record-with-unknown-record.mjs +10 -19
  7. package/dist/functions/ast-transformers/replace-record-with-unknown-record.mjs.map +1 -1
  8. package/dist/functions/ast-transformers/transform-source-code.d.mts.map +1 -1
  9. package/dist/functions/ast-transformers/transform-source-code.mjs +101 -49
  10. package/dist/functions/ast-transformers/transform-source-code.mjs.map +1 -1
  11. package/dist/functions/functions/should-avoid-parentheses-for-readonly.d.mts.map +1 -1
  12. package/dist/functions/functions/should-avoid-parentheses-for-readonly.mjs +1 -4
  13. package/dist/functions/functions/should-avoid-parentheses-for-readonly.mjs.map +1 -1
  14. package/dist/functions/functions/wrap-with-parentheses.d.mts.map +1 -1
  15. package/dist/functions/functions/wrap-with-parentheses.mjs +3 -4
  16. package/dist/functions/functions/wrap-with-parentheses.mjs.map +1 -1
  17. package/package.json +42 -41
  18. package/src/functions/ast-transformers/append-as-const.mts +22 -20
  19. package/src/functions/ast-transformers/convert-to-readonly.mts +96 -1
  20. package/src/functions/ast-transformers/convert-to-readonly.test.mts +205 -10
  21. package/src/functions/ast-transformers/enable-no-unchecked-indexed-access.test.mts +1 -1
  22. package/src/functions/ast-transformers/replace-record-with-unknown-record.mts +13 -22
  23. package/src/functions/ast-transformers/transform-source-code.mts +158 -57
  24. package/src/functions/ast-transformers/transform-source-code.test.mts +123 -0
  25. package/src/functions/functions/should-avoid-parentheses-for-readonly.mts +1 -5
  26. package/src/functions/functions/wrap-with-parentheses.mts +3 -5
@@ -0,0 +1,123 @@
1
+ import { replaceAnyWithUnknownTransformer } from './replace-any-with-unknown.mjs';
2
+ import { transformSourceCode } from './transform-source-code.mjs';
3
+
4
+ const body = 'export const x: any = 1;\n';
5
+
6
+ const transformedBody = 'export const x: unknown = 1;\n';
7
+
8
+ const transform = (code: string): string =>
9
+ transformSourceCode(code, false, [replaceAnyWithUnknownTransformer()]);
10
+
11
+ describe('transformSourceCode file-level ignore comments', () => {
12
+ test.each([
13
+ {
14
+ name: 'no ignore comment',
15
+ header: '',
16
+ skipped: false,
17
+ },
18
+ {
19
+ name: 'ignore all transformers',
20
+ header: '/* transformer-ignore */\n',
21
+ skipped: true,
22
+ },
23
+ {
24
+ name: 'ignore all transformers without spaces',
25
+ header: '/*transformer-ignore*/\n',
26
+ skipped: true,
27
+ },
28
+ {
29
+ name: 'ignore the running transformer',
30
+ header: '/* transformer-ignore replace-any-with-unknown */\n',
31
+ skipped: true,
32
+ },
33
+ {
34
+ name: 'ignore the running transformer among others',
35
+ header:
36
+ '/* transformer-ignore append-as-const, replace-any-with-unknown */\n',
37
+ skipped: true,
38
+ },
39
+ {
40
+ name: 'ignore another transformer only',
41
+ header: '/* transformer-ignore append-as-const */\n',
42
+ skipped: false,
43
+ },
44
+ {
45
+ name: 'ts-codemod-ignore prefix',
46
+ header: '/* ts-codemod-ignore */\n',
47
+ skipped: true,
48
+ },
49
+ {
50
+ name: 'codemod-ignore prefix',
51
+ header: '/* codemod-ignore replace-any-with-unknown */\n',
52
+ skipped: true,
53
+ },
54
+ {
55
+ name: 'transform-ignore prefix',
56
+ header: '/* transform-ignore */\n',
57
+ skipped: true,
58
+ },
59
+ {
60
+ name: 'names on the first line and the terminator on the next',
61
+ header: '/* transformer-ignore replace-any-with-unknown\n */\n',
62
+ skipped: true,
63
+ },
64
+ {
65
+ name: 'prefix on the first line and names on the next',
66
+ header: '/*\n transformer-ignore\n replace-any-with-unknown */\n',
67
+ skipped: true,
68
+ },
69
+ {
70
+ name: 'names spanning several lines are not an ignore comment',
71
+ header: '/* transformer-ignore append-as-const\n note */\n',
72
+ skipped: false,
73
+ },
74
+ {
75
+ name: 'an unterminated comment is not an ignore comment',
76
+ header: '// /* transformer-ignore\n',
77
+ skipped: false,
78
+ },
79
+ {
80
+ name: 'a later well-formed comment is found after a malformed one',
81
+ header:
82
+ '/* transformer-ignore a\n b */\n/* transformer-ignore replace-any-with-unknown */\n',
83
+ skipped: true,
84
+ },
85
+ {
86
+ name: 'prefixes are tried in order, not by position',
87
+ header:
88
+ '/* codemod-ignore */\n/* transformer-ignore append-as-const */\n',
89
+ skipped: false,
90
+ },
91
+ ] as const)('$name', ({ header, skipped }) => {
92
+ assert.strictEqual(
93
+ transform(`${header}${body}`),
94
+ `${header}${skipped ? body : transformedBody}`,
95
+ );
96
+ });
97
+
98
+ test(
99
+ 'finishes in linear time on long whitespace runs in unterminated comments',
100
+ { timeout: 2000 },
101
+ () => {
102
+ const header = `/* transformer-ignore${' '.repeat(2000)}x\nnote */\n`;
103
+
104
+ assert.strictEqual(
105
+ transform(`${header}${body}`),
106
+ `${header}${transformedBody}`,
107
+ );
108
+ },
109
+ );
110
+
111
+ test(
112
+ 'finishes in linear time on many unterminated comments in one line',
113
+ { timeout: 2000 },
114
+ () => {
115
+ const header = `export const s = \`${'/* transformer-ignore x '.repeat(10_000)}\`;\n`;
116
+
117
+ assert.strictEqual(
118
+ transform(`${header}${body}`),
119
+ `${header}${transformedBody}`,
120
+ );
121
+ },
122
+ );
123
+ });
@@ -16,13 +16,9 @@ import * as tsm from 'ts-morph';
16
16
  export const shouldAvoidParenthesesForReadonly = (node: tsm.Node): boolean => {
17
17
  const parent = node.getParent();
18
18
 
19
- if (parent === undefined) {
20
- return false;
21
- }
22
-
23
19
  // Check if parent is TypePredicate: x is T
24
20
  // In this case, we MUST avoid parentheses: x is readonly T (not x is (readonly T))
25
21
  // because `x is (readonly T)` is a syntax error
26
22
  // For all other cases, keep parentheses for safety
27
- return parent.isKind(tsm.SyntaxKind.TypePredicate);
23
+ return parent?.isKind(tsm.SyntaxKind.TypePredicate) ?? false;
28
24
  };
@@ -76,9 +76,7 @@ const isWrappedWithParentheses = (str: string): boolean => {
76
76
  export const wrapWithParentheses = (nodeStr: string): string => {
77
77
  const trimmed = nodeStr.trim();
78
78
 
79
- if (isWrappedWithParentheses(trimmed)) {
80
- return trimmed;
81
- }
82
-
83
- return `(${trimmed})` as const;
79
+ return isWrappedWithParentheses(trimmed)
80
+ ? trimmed
81
+ : (`(${trimmed})` as const);
84
82
  };