ts-codemod-lib 3.4.3 → 3.5.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.
- package/dist/functions/ast-transformers/append-as-const.d.mts.map +1 -1
- package/dist/functions/ast-transformers/append-as-const.mjs +38 -6
- package/dist/functions/ast-transformers/append-as-const.mjs.map +1 -1
- package/dist/functions/ast-transformers/convert-interface-to-type.d.mts.map +1 -1
- package/dist/functions/ast-transformers/convert-interface-to-type.mjs +2 -1
- package/dist/functions/ast-transformers/convert-interface-to-type.mjs.map +1 -1
- package/dist/functions/ast-transformers/convert-to-readonly.d.mts.map +1 -1
- package/dist/functions/ast-transformers/convert-to-readonly.mjs +2 -2
- package/dist/functions/ast-transformers/convert-to-readonly.mjs.map +1 -1
- package/dist/functions/ast-transformers/enable-no-unchecked-indexed-access.d.mts +1 -1
- package/dist/functions/ast-transformers/enable-no-unchecked-indexed-access.mjs +4 -0
- package/dist/functions/ast-transformers/enable-no-unchecked-indexed-access.mjs.map +1 -1
- package/dist/functions/ast-transformers/replace-any-with-unknown.d.mts.map +1 -1
- package/dist/functions/ast-transformers/replace-any-with-unknown.mjs +3 -2
- package/dist/functions/ast-transformers/replace-any-with-unknown.mjs.map +1 -1
- package/dist/functions/ast-transformers/replace-record-with-unknown-record.d.mts.map +1 -1
- package/dist/functions/ast-transformers/replace-record-with-unknown-record.mjs +6 -5
- package/dist/functions/ast-transformers/replace-record-with-unknown-record.mjs.map +1 -1
- package/dist/functions/functions/has-disable-next-line-comment.d.mts.map +1 -1
- package/dist/functions/functions/has-disable-next-line-comment.mjs +56 -4
- package/dist/functions/functions/has-disable-next-line-comment.mjs.map +1 -1
- package/dist/functions/functions/wrap-with-parentheses.d.mts +5 -5
- package/dist/functions/utils/index.d.mts +1 -0
- package/dist/functions/utils/index.d.mts.map +1 -1
- package/dist/functions/utils/index.mjs +1 -0
- package/dist/functions/utils/index.mjs.map +1 -1
- package/dist/functions/utils/replace-node-with-text.d.mts +21 -0
- package/dist/functions/utils/replace-node-with-text.d.mts.map +1 -0
- package/dist/functions/utils/replace-node-with-text.mjs +22 -0
- package/dist/functions/utils/replace-node-with-text.mjs.map +1 -0
- package/dist/functions/utils/replace-with-debug.d.mts.map +1 -1
- package/dist/functions/utils/replace-with-debug.mjs +2 -1
- package/dist/functions/utils/replace-with-debug.mjs.map +1 -1
- package/package.json +15 -15
- package/src/functions/ast-transformers/append-as-const.mts +57 -6
- package/src/functions/ast-transformers/append-as-const.test.mts +119 -0
- package/src/functions/ast-transformers/convert-interface-to-type.mts +2 -1
- package/src/functions/ast-transformers/convert-to-readonly.mts +5 -2
- package/src/functions/ast-transformers/convert-to-readonly.test.mts +9 -20
- package/src/functions/ast-transformers/enable-no-unchecked-indexed-access.mts +5 -1
- package/src/functions/ast-transformers/enable-no-unchecked-indexed-access.test.mts +36 -0
- package/src/functions/ast-transformers/replace-any-with-unknown.mts +3 -2
- package/src/functions/ast-transformers/replace-record-with-unknown-record.mts +7 -5
- package/src/functions/functions/has-disable-next-line-comment.mts +79 -6
- package/src/functions/functions/has-disable-next-line-comment.test.mts +96 -0
- package/src/functions/functions/wrap-with-parentheses.mts +5 -5
- package/src/functions/utils/index.mts +1 -0
- package/src/functions/utils/replace-node-with-text.mts +37 -0
- package/src/functions/utils/replace-node-with-text.test.mts +89 -0
- package/src/functions/utils/replace-with-debug.mts +2 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"has-disable-next-line-comment.d.mts","sourceRoot":"","sources":["../../../src/functions/functions/has-disable-next-line-comment.mts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"has-disable-next-line-comment.d.mts","sourceRoot":"","sources":["../../../src/functions/functions/has-disable-next-line-comment.mts"],"names":[],"mappings":"AACA,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAGhC;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,yBAAyB,SAC9B,GAAG,CAAC,IAAI,oBACI,MAAM,KACvB,OA8EF,CAAC"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Arr } from 'ts-data-forge';
|
|
1
2
|
import * as tsm from 'ts-morph';
|
|
2
3
|
import { IGNORE_LINE_COMMENT_PREFIXES } from '../constants/index.mjs';
|
|
3
4
|
|
|
@@ -15,20 +16,25 @@ import { IGNORE_LINE_COMMENT_PREFIXES } from '../constants/index.mjs';
|
|
|
15
16
|
|
|
16
17
|
|
|
17
18
|
export const hasDisableNextLineComment = (node, transformerName) => {
|
|
18
|
-
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
const leadingCommentRanges = node.getLeadingCommentRanges();
|
|
22
|
+
if (Arr.isEmpty(leadingCommentRanges)) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
const lineNumberAt = lineNumberLookup(node.getSourceFile().compilerNode);
|
|
26
|
+
const nodeStartLine = lineNumberAt(node.getStart());
|
|
19
27
|
|
|
20
28
|
if (nodeStartLine <= 1) {
|
|
21
29
|
return false;
|
|
22
30
|
}
|
|
23
|
-
const sourceFile = node.getSourceFile();
|
|
24
|
-
const leadingCommentRanges = node.getLeadingCommentRanges();
|
|
25
31
|
|
|
26
32
|
for (let mut_i = leadingCommentRanges.length - 1; mut_i >= 0; mut_i--) {
|
|
27
33
|
|
|
28
34
|
const commentRange = leadingCommentRanges[mut_i];
|
|
29
35
|
|
|
30
36
|
const commentEndPos = commentRange.getEnd();
|
|
31
|
-
const commentEndLine =
|
|
37
|
+
const commentEndLine = lineNumberAt(commentEndPos);
|
|
32
38
|
|
|
33
39
|
if (nodeStartLine === commentEndLine + 1) {
|
|
34
40
|
|
|
@@ -69,4 +75,50 @@ export const hasDisableNextLineComment = (node, transformerName) => {
|
|
|
69
75
|
}
|
|
70
76
|
return false;
|
|
71
77
|
};
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
const lineNumberLookup = (
|
|
91
|
+
|
|
92
|
+
sourceFile) => {
|
|
93
|
+
const cached = newlinePositionsCache.get(sourceFile);
|
|
94
|
+
const newlinePositions = cached ?? newlinePositionsOf(sourceFile.text);
|
|
95
|
+
if (cached === undefined) {
|
|
96
|
+
newlinePositionsCache.set(sourceFile, newlinePositions);
|
|
97
|
+
}
|
|
98
|
+
return (pos) => countLessThan(newlinePositions, pos) + 1;
|
|
99
|
+
};
|
|
100
|
+
const newlinePositionsCache = new WeakMap();
|
|
101
|
+
const newlinePositionsOf = (text) => {
|
|
102
|
+
const mut_positions = [];
|
|
103
|
+
for (let mut_index = text.indexOf('\n'); mut_index !== -1; mut_index = text.indexOf('\n', mut_index + 1)) {
|
|
104
|
+
mut_positions.push(mut_index);
|
|
105
|
+
}
|
|
106
|
+
return mut_positions;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const countLessThan = (sorted, value) => {
|
|
110
|
+
let mut_low = 0;
|
|
111
|
+
let mut_high = sorted.length;
|
|
112
|
+
while (mut_low < mut_high) {
|
|
113
|
+
const mid = Math.floor((mut_low + mut_high) / 2);
|
|
114
|
+
|
|
115
|
+
if (sorted[mid] < value) {
|
|
116
|
+
mut_low = mid + 1;
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
mut_high = mid;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return mut_low;
|
|
123
|
+
};
|
|
72
124
|
//# sourceMappingURL=has-disable-next-line-comment.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"has-disable-next-line-comment.mjs","sourceRoot":"","sources":["../../../src/functions/functions/has-disable-next-line-comment.mts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAChC,OAAO,EAAE,4BAA4B,EAAE,MAAM,wBAAwB,CAAC;AAEtE;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,CACvC,IAAc,EACd,eAAwB,EACf,EAAE;IACX,MAAM,
|
|
1
|
+
{"version":3,"file":"has-disable-next-line-comment.mjs","sourceRoot":"","sources":["../../../src/functions/functions/has-disable-next-line-comment.mts"],"names":[],"mappings":"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"}
|
|
@@ -6,17 +6,17 @@
|
|
|
6
6
|
* @example
|
|
7
7
|
*
|
|
8
8
|
* ```ts
|
|
9
|
-
* assert.
|
|
9
|
+
* assert.strictEqual(wrapWithParentheses('A'), '(A)');
|
|
10
10
|
*
|
|
11
11
|
* // already wrapped, so not '((A))'
|
|
12
|
-
* assert.
|
|
12
|
+
* assert.strictEqual(wrapWithParentheses('(A)'), '(A)');
|
|
13
13
|
*
|
|
14
|
-
* assert.
|
|
14
|
+
* assert.strictEqual(wrapWithParentheses('A | B'), '(A | B)');
|
|
15
15
|
*
|
|
16
|
-
* assert.
|
|
16
|
+
* assert.strictEqual(wrapWithParentheses('(A | B)'), '(A | B)');
|
|
17
17
|
*
|
|
18
18
|
* // the outer parentheses do not span everything, so a pair is added
|
|
19
|
-
* assert.
|
|
19
|
+
* assert.strictEqual(wrapWithParentheses('(A) | (B)'), '((A) | (B))');
|
|
20
20
|
* ```
|
|
21
21
|
*/
|
|
22
22
|
export declare const wrapWithParentheses: (nodeStr: string) => string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../../../src/functions/utils/index.mts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../../../src/functions/utils/index.mts"],"names":[],"mappings":"AAAA,cAAc,8BAA8B,CAAC;AAC7C,cAAc,0BAA0B,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","sourceRoot":"","sources":["../../../src/functions/utils/index.mts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.mjs","sourceRoot":"","sources":["../../../src/functions/utils/index.mts"],"names":[],"mappings":"AAAA,cAAc,8BAA8B,CAAC;AAC7C,cAAc,0BAA0B,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type * as tsm from 'ts-morph';
|
|
2
|
+
/**
|
|
3
|
+
* `node.replaceWithText(newNodeText)`, sparing a one-line text the indentation
|
|
4
|
+
* lookup.
|
|
5
|
+
*
|
|
6
|
+
* `replaceWithText` writes the text through a writer whose indentation it
|
|
7
|
+
* looks up first, for the lines after the first one, and the lookup reparses
|
|
8
|
+
* the whole file in the language service: on top of the reparse every
|
|
9
|
+
* replacement makes anyway, once per replacement, so it is quadratic in the
|
|
10
|
+
* size of the file. A text with no `\n` has no line after the first, the
|
|
11
|
+
* indentation is never applied, and a plain writer from the same project
|
|
12
|
+
* writes the same text — so the lookup is skipped. A multi-line text takes
|
|
13
|
+
* the original path.
|
|
14
|
+
*
|
|
15
|
+
* The writer is passed as `replaceWithText`'s second argument, which its
|
|
16
|
+
* declaration leaves out and ts-morph passes itself when it sets an
|
|
17
|
+
* expression. Were it ever dropped, the method would look the indentation up
|
|
18
|
+
* as before: slower, never different.
|
|
19
|
+
*/
|
|
20
|
+
export declare const replaceNodeWithText: (node: tsm.Node, newNodeText: string) => tsm.Node;
|
|
21
|
+
//# sourceMappingURL=replace-node-with-text.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"replace-node-with-text.d.mts","sourceRoot":"","sources":["../../../src/functions/utils/replace-node-with-text.mts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,GAAG,MAAM,UAAU,CAAC;AAErC;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,mBAAmB,SACxB,GAAG,CAAC,IAAI,eACD,MAAM,KAClB,GAAG,CAAC,IAOA,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
export const replaceNodeWithText = (node, newNodeText) => newNodeText.includes('\n')
|
|
20
|
+
? node.replaceWithText(newNodeText)
|
|
21
|
+
: node.replaceWithText.call(node, newNodeText, node.getProject().createWriter());
|
|
22
|
+
//# sourceMappingURL=replace-node-with-text.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"replace-node-with-text.mjs","sourceRoot":"","sources":["../../../src/functions/utils/replace-node-with-text.mts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CACjC,IAAc,EACd,WAAmB,EACT,EAAE,CACZ,WAAW,CAAC,QAAQ,CAAC,IAAI,CAAC;IACxB,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,WAAW,CAAC;IACnC,CAAC,CAAE,IAAI,CAAC,eAA6C,CAAC,IAAI,CACtD,IAAI,EACJ,WAAW,EACX,IAAI,CAAC,UAAU,EAAE,CAAC,YAAY,EAAE,CACjC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"replace-with-debug.d.mts","sourceRoot":"","sources":["../../../src/functions/utils/replace-with-debug.mts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,GAAG,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"replace-with-debug.d.mts","sourceRoot":"","sources":["../../../src/functions/utils/replace-with-debug.mts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,GAAG,MAAM,UAAU,CAAC;AAGrC,eAAO,MAAM,yBAAyB,SAC9B,GAAG,CAAC,IAAI,eACD,MAAM,KAClB,IAIF,CAAC"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { replaceNodeWithText } from './replace-node-with-text.mjs';
|
|
1
2
|
export const replaceNodeWithDebugPrint = (node, newNodeText) => {
|
|
2
3
|
console.debug(`${node.getText()} -> ${newNodeText}`);
|
|
3
|
-
node
|
|
4
|
+
replaceNodeWithText(node, newNodeText);
|
|
4
5
|
};
|
|
5
6
|
//# sourceMappingURL=replace-with-debug.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"replace-with-debug.mjs","sourceRoot":"","sources":["../../../src/functions/utils/replace-with-debug.mts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"replace-with-debug.mjs","sourceRoot":"","sources":["../../../src/functions/utils/replace-with-debug.mts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AAEnE,MAAM,CAAC,MAAM,yBAAyB,GAAG,CACvC,IAAc,EACd,WAAmB,EACb,EAAE;IACR,OAAO,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,OAAO,EAAE,OAAO,WAAW,EAAE,CAAC,CAAC;IAErD,mBAAmB,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;AACzC,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ts-codemod-lib",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.5.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typescript",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"LICENSE"
|
|
39
39
|
],
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"ts-data-forge": "^14.
|
|
41
|
+
"ts-data-forge": "^14.8.0",
|
|
42
42
|
"ts-morph": "^28.0.0",
|
|
43
43
|
"ts-type-forge": "^9.2.2"
|
|
44
44
|
},
|
|
@@ -51,13 +51,13 @@
|
|
|
51
51
|
"dedent": "1.7.2",
|
|
52
52
|
"esbuild": "0.28.2",
|
|
53
53
|
"eslint": "10.12.0",
|
|
54
|
-
"eslint-config-typed": "5.
|
|
55
|
-
"eslint-plugin-ts-data-forge": "0.
|
|
56
|
-
"eslint-plugin-ts-fortress": "1.3.
|
|
57
|
-
"eslint-plugin-ts-type-forge": "0.8.
|
|
54
|
+
"eslint-config-typed": "5.19.0",
|
|
55
|
+
"eslint-plugin-ts-data-forge": "0.9.0",
|
|
56
|
+
"eslint-plugin-ts-fortress": "1.3.4",
|
|
57
|
+
"eslint-plugin-ts-type-forge": "0.8.9",
|
|
58
58
|
"jiti": "2.7.0",
|
|
59
59
|
"npm-run-all2": "9.0.3",
|
|
60
|
-
"playwright": "1.
|
|
60
|
+
"playwright": "1.64.0",
|
|
61
61
|
"prettier": "3.9.9",
|
|
62
62
|
"prettier-plugin-organize-imports": "4.3.0",
|
|
63
63
|
"prettier-plugin-packagejson": "3.0.2",
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
"typedoc-github-theme": "0.4.0",
|
|
68
68
|
"typescript": "6.0.3",
|
|
69
69
|
"typescript-native": "npm:typescript@7.0.2",
|
|
70
|
-
"vite": "8.3.
|
|
70
|
+
"vite": "8.3.3",
|
|
71
71
|
"vitest": "5.0.3"
|
|
72
72
|
},
|
|
73
73
|
"engines": {
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
"pnpm": ">=9.0.0"
|
|
76
76
|
},
|
|
77
77
|
"volta": {
|
|
78
|
-
"node": "26.
|
|
78
|
+
"node": "26.12.0"
|
|
79
79
|
},
|
|
80
80
|
"publishConfig": {
|
|
81
81
|
"access": "public",
|
|
@@ -86,11 +86,11 @@
|
|
|
86
86
|
"check:cspell": "cspell \"**\" --gitignore --gitignore-root ../../ --no-progress",
|
|
87
87
|
"check:dist": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ./scripts/cmd/check-dist.mts",
|
|
88
88
|
"check:ext": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ./scripts/cmd/check-ext.mts",
|
|
89
|
-
"check:lint": "oxlint . && eslint .",
|
|
90
|
-
"check:test": "pnpm run z:vitest:node run",
|
|
91
|
-
"check:test:browser": "pnpm run z:vitest --project='Browser' run",
|
|
92
|
-
"check:test:cov": "pnpm run z:vitest:node run --coverage",
|
|
93
|
-
"check:types": "node ../../node_modules/typescript-native/bin/tsc --noEmit",
|
|
89
|
+
"check:lint": "node ../../tools/scripts/cmd/assert-deps-built.mts && oxlint . && eslint .",
|
|
90
|
+
"check:test": "node ../../tools/scripts/cmd/assert-deps-built.mts && pnpm run z:vitest:node run",
|
|
91
|
+
"check:test:browser": "node ../../tools/scripts/cmd/assert-deps-built.mts && pnpm run z:vitest --project='Browser' run",
|
|
92
|
+
"check:test:cov": "node ../../tools/scripts/cmd/assert-deps-built.mts && pnpm run z:vitest:node run --coverage",
|
|
93
|
+
"check:types": "node ../../tools/scripts/cmd/assert-deps-built.mts && node ../../node_modules/typescript-native/bin/tsc --noEmit",
|
|
94
94
|
"check-all": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ./scripts/cmd/check-all.mts",
|
|
95
95
|
"clean": "pnpm run /clean:.*/",
|
|
96
96
|
"clean:logs": "npx rimraf ./*.log",
|
|
@@ -102,7 +102,7 @@
|
|
|
102
102
|
"fix:fmt": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ../../libs/ts-repo-utils/src/cmd/format-uncommitted.mts --cwd .",
|
|
103
103
|
"fix:fmt:diff": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ../../libs/ts-repo-utils/src/cmd/format-diff-from.mts origin/main --cwd .",
|
|
104
104
|
"fix:fmt:full": "prettier --write .",
|
|
105
|
-
"fix:lint": "oxlint . --fix && eslint . --fix",
|
|
105
|
+
"fix:lint": "node ../../tools/scripts/cmd/assert-deps-built.mts && oxlint . --fix && eslint . --fix",
|
|
106
106
|
"fmt": "pnpm run fix:fmt",
|
|
107
107
|
"gen": "run-s gen:index gen:readme gen:jsdoc",
|
|
108
108
|
"gen:index": "run-s gen:index:src fix:fmt",
|
|
@@ -5,7 +5,10 @@ import {
|
|
|
5
5
|
hasDisableNextLineComment,
|
|
6
6
|
isAsConstNode,
|
|
7
7
|
} from '../functions/index.mjs';
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
replaceNodeWithDebugPrint,
|
|
10
|
+
replaceNodeWithText,
|
|
11
|
+
} from '../utils/index.mjs';
|
|
9
12
|
import type { TsMorphTransformer } from './types.mjs';
|
|
10
13
|
|
|
11
14
|
const TRANSFORMER_NAME = 'append-as-const';
|
|
@@ -27,7 +30,7 @@ export const appendAsConstTransformer = (
|
|
|
27
30
|
replaceNode:
|
|
28
31
|
options?.debug === true
|
|
29
32
|
? replaceNodeWithDebugPrint
|
|
30
|
-
: (node, newNodeText) => node
|
|
33
|
+
: (node, newNodeText) => replaceNodeWithText(node, newNodeText),
|
|
31
34
|
} as const;
|
|
32
35
|
|
|
33
36
|
return {
|
|
@@ -199,7 +202,10 @@ const transformNode = (
|
|
|
199
202
|
}
|
|
200
203
|
|
|
201
204
|
// Skip already type asserted nodes
|
|
202
|
-
if (
|
|
205
|
+
if (
|
|
206
|
+
(tsm.Node.isAsExpression(node) || tsm.Node.isTypeAssertion(node)) &&
|
|
207
|
+
!isConstAssertionNode(node)
|
|
208
|
+
) {
|
|
203
209
|
return;
|
|
204
210
|
}
|
|
205
211
|
|
|
@@ -308,8 +314,21 @@ const transformNode = (
|
|
|
308
314
|
return;
|
|
309
315
|
}
|
|
310
316
|
|
|
317
|
+
// `<const>x` is handled as the `x as const` it means. Appending `as const`
|
|
318
|
+
// to `x` in place would read as `(<const>x) as const`, and every position
|
|
319
|
+
// that reaches here already takes an `as` expression.
|
|
320
|
+
if (tsm.Node.isTypeAssertion(node) && isConstAssertionNode(node)) {
|
|
321
|
+
const asConstText = `${node.getExpression().getText()} as const` as const;
|
|
322
|
+
|
|
323
|
+
options.debugPrint(`${node.getText()} -> ${asConstText}`);
|
|
324
|
+
|
|
325
|
+
transformNode(replaceNodeWithText(node, asConstText), context, options);
|
|
326
|
+
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
|
|
311
330
|
// `as const` node
|
|
312
|
-
if (
|
|
331
|
+
if (isConstAssertionNode(node)) {
|
|
313
332
|
if (context.isDirectUnderConstInitializer) {
|
|
314
333
|
// In const variable declarations, remove `as const` first and then re-append it later if needed
|
|
315
334
|
|
|
@@ -424,7 +443,9 @@ const removeAsConstInConstTypeParameterArgs = (
|
|
|
424
443
|
|
|
425
444
|
// Cheap syntactic pre-check before touching the type checker
|
|
426
445
|
if (
|
|
427
|
-
args.every(
|
|
446
|
+
args.every(
|
|
447
|
+
(arg) => !isConstAssertionNode(skipParenthesizedExpressions(arg)),
|
|
448
|
+
)
|
|
428
449
|
) {
|
|
429
450
|
continue;
|
|
430
451
|
}
|
|
@@ -487,7 +508,7 @@ const removeAsConstArgsOfCall = (
|
|
|
487
508
|
// `f(([1] as const))` is the same argument as `f([1] as const)`.
|
|
488
509
|
const asConst = skipParenthesizedExpressions(argument);
|
|
489
510
|
|
|
490
|
-
if (!
|
|
511
|
+
if (!isConstAssertionNode(asConst)) {
|
|
491
512
|
continue;
|
|
492
513
|
}
|
|
493
514
|
|
|
@@ -517,6 +538,31 @@ const removeAsConstArgsOfCall = (
|
|
|
517
538
|
}
|
|
518
539
|
};
|
|
519
540
|
|
|
541
|
+
/**
|
|
542
|
+
* `x as const`, or `<const>x`, the angle-bracket spelling TypeScript treats
|
|
543
|
+
* identically (outside `.tsx`, where it does not parse).
|
|
544
|
+
*/
|
|
545
|
+
const isConstAssertionNode = (
|
|
546
|
+
node: tsm.Node,
|
|
547
|
+
): node is tsm.AsExpression | tsm.TypeAssertion => {
|
|
548
|
+
if (isAsConstNode(node)) {
|
|
549
|
+
return true;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
if (!tsm.Node.isTypeAssertion(node)) {
|
|
553
|
+
return false;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
const typeNode = node.getTypeNode();
|
|
557
|
+
|
|
558
|
+
return (
|
|
559
|
+
typeNode !== undefined &&
|
|
560
|
+
typeNode.isKind(tsm.SyntaxKind.TypeReference) &&
|
|
561
|
+
typeNode.getTypeName().getText() === 'const' &&
|
|
562
|
+
Arr.isEmpty(typeNode.getTypeArguments())
|
|
563
|
+
);
|
|
564
|
+
};
|
|
565
|
+
|
|
520
566
|
/** `node` with the parentheses written around it taken off. */
|
|
521
567
|
const skipParenthesizedExpressions = (node: tsm.Node): tsm.Node =>
|
|
522
568
|
tsm.Node.isParenthesizedExpression(node)
|
|
@@ -541,6 +587,11 @@ const resolveSingleSignatureCallTarget = (
|
|
|
541
587
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
542
588
|
call: tsm.CallExpression,
|
|
543
589
|
): ResolvedCallTarget | undefined => {
|
|
590
|
+
// The callee's type is read as the call sees it, after any `as`,
|
|
591
|
+
// `satisfies` or `!` around it: that is the signature the argument is
|
|
592
|
+
// inferred against, and an `as const` has no run-time effect for an inner
|
|
593
|
+
// layer to contradict.
|
|
594
|
+
//
|
|
544
595
|
// 0 signatures: the callee is unresolved (e.g. imported, since each file
|
|
545
596
|
// is transformed in an isolated single-file project).
|
|
546
597
|
// 2+ signatures: overloads, where removing `as const` could change which
|
|
@@ -894,6 +894,125 @@ describe(appendAsConstTransformer, () => {
|
|
|
894
894
|
])('$name', testFn);
|
|
895
895
|
});
|
|
896
896
|
|
|
897
|
+
describe('Callee judged by its type at the call, through type wrappers', () => {
|
|
898
|
+
test.each([
|
|
899
|
+
{
|
|
900
|
+
name: 'widening as to a const type parameter signature should remove as const',
|
|
901
|
+
source: dedent`
|
|
902
|
+
const g = <T,>(x: T): T => x;
|
|
903
|
+
const a = (g as <const T>(x: T) => T)([1, 2] as const);
|
|
904
|
+
`,
|
|
905
|
+
expected: dedent`
|
|
906
|
+
const g = <T,>(x: T): T => x;
|
|
907
|
+
const a = (g as <const T>(x: T) => T)([1, 2]);
|
|
908
|
+
`,
|
|
909
|
+
},
|
|
910
|
+
{
|
|
911
|
+
name: 'as to a non-const type parameter signature should keep as const',
|
|
912
|
+
source: dedent`
|
|
913
|
+
const g = <const T,>(x: T): T => x;
|
|
914
|
+
const a = (g as <T>(x: T) => T)([1, 2] as const);
|
|
915
|
+
`,
|
|
916
|
+
expected: dedent`
|
|
917
|
+
const g = <const T,>(x: T): T => x;
|
|
918
|
+
const a = (g as <T>(x: T) => T)([1, 2] as const);
|
|
919
|
+
`,
|
|
920
|
+
},
|
|
921
|
+
{
|
|
922
|
+
name: 'satisfies keeps the callee type, so as const should be removed',
|
|
923
|
+
source: dedent`
|
|
924
|
+
const g = <const T,>(x: T): T => x;
|
|
925
|
+
const a = (g satisfies unknown)([1, 2] as const);
|
|
926
|
+
`,
|
|
927
|
+
expected: dedent`
|
|
928
|
+
const g = <const T,>(x: T): T => x;
|
|
929
|
+
const a = (g satisfies unknown)([1, 2]);
|
|
930
|
+
`,
|
|
931
|
+
},
|
|
932
|
+
{
|
|
933
|
+
name: 'non-null asserted callee should remove as const',
|
|
934
|
+
source: dedent`
|
|
935
|
+
declare const g: (<const T>(x: T) => T) | undefined;
|
|
936
|
+
const a = g!([1, 2] as const);
|
|
937
|
+
`,
|
|
938
|
+
expected: dedent`
|
|
939
|
+
declare const g: (<const T>(x: T) => T) | undefined;
|
|
940
|
+
const a = g!([1, 2]);
|
|
941
|
+
`,
|
|
942
|
+
},
|
|
943
|
+
{
|
|
944
|
+
name: 'parenthesized callee should remove as const',
|
|
945
|
+
source: dedent`
|
|
946
|
+
const g = <const T,>(x: T): T => x;
|
|
947
|
+
const a = ((g))([1, 2] as const);
|
|
948
|
+
`,
|
|
949
|
+
expected: dedent`
|
|
950
|
+
const g = <const T,>(x: T): T => x;
|
|
951
|
+
const a = ((g))([1, 2]);
|
|
952
|
+
`,
|
|
953
|
+
},
|
|
954
|
+
])('$name', testFn);
|
|
955
|
+
});
|
|
956
|
+
|
|
957
|
+
describe('Angle-bracket const assertions (<const>x)', () => {
|
|
958
|
+
test.each([
|
|
959
|
+
{
|
|
960
|
+
name: 'should be removed inside an array literal that gets as const',
|
|
961
|
+
source: 'const c = [<const>[1], 2];',
|
|
962
|
+
expected: 'const c = [[1], 2] as const;',
|
|
963
|
+
},
|
|
964
|
+
{
|
|
965
|
+
name: 'should be removed inside an object literal that gets as const',
|
|
966
|
+
source: 'const o = { a: <const>[1] };',
|
|
967
|
+
expected: 'const o = { a: [1] } as const;',
|
|
968
|
+
},
|
|
969
|
+
{
|
|
970
|
+
name: 'should be spelled as const on a const initializer',
|
|
971
|
+
source: 'const b = <const>[1, 2];',
|
|
972
|
+
expected: 'const b = [1, 2] as const;',
|
|
973
|
+
},
|
|
974
|
+
{
|
|
975
|
+
name: 'should be removed from a primitive const initializer',
|
|
976
|
+
source: "const s = <const>'a';",
|
|
977
|
+
expected: "const s = 'a';",
|
|
978
|
+
},
|
|
979
|
+
{
|
|
980
|
+
name: 'should be removed inside an outer as const',
|
|
981
|
+
source: 'const n = [<const>[1], 2] as const;',
|
|
982
|
+
expected: 'const n = [[1], 2] as const;',
|
|
983
|
+
},
|
|
984
|
+
{
|
|
985
|
+
name: 'const type parameter should remove it from an argument',
|
|
986
|
+
source: dedent`
|
|
987
|
+
function f<const T>(x: T): T { return x; }
|
|
988
|
+
const a = f(<const>[1, 2]);
|
|
989
|
+
const b = f((<const>[1, 2]));
|
|
990
|
+
`,
|
|
991
|
+
expected: dedent`
|
|
992
|
+
function f<const T>(x: T): T { return x; }
|
|
993
|
+
const a = f([1, 2]);
|
|
994
|
+
const b = f([1, 2]);
|
|
995
|
+
`,
|
|
996
|
+
},
|
|
997
|
+
{
|
|
998
|
+
name: 'non-const type parameter should keep it in an argument',
|
|
999
|
+
source: dedent`
|
|
1000
|
+
function g<T>(x: T): T { return x; }
|
|
1001
|
+
const a = g(<const>[1, 2]);
|
|
1002
|
+
`,
|
|
1003
|
+
expected: dedent`
|
|
1004
|
+
function g<T>(x: T): T { return x; }
|
|
1005
|
+
const a = g(<const>[1, 2]);
|
|
1006
|
+
`,
|
|
1007
|
+
},
|
|
1008
|
+
{
|
|
1009
|
+
name: 'other angle-bracket assertions should be left as they are',
|
|
1010
|
+
source: 'const d = <readonly number[]>[1, 2];',
|
|
1011
|
+
expected: 'const d = <readonly number[]>[1, 2];',
|
|
1012
|
+
},
|
|
1013
|
+
])('$name', testFn);
|
|
1014
|
+
});
|
|
1015
|
+
|
|
897
1016
|
describe('ignorePrefixes option (mut_ prefix)', () => {
|
|
898
1017
|
test.each([
|
|
899
1018
|
{
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { Arr } from 'ts-data-forge';
|
|
4
4
|
import type * as tsm from 'ts-morph';
|
|
5
5
|
import { hasDisableNextLineComment } from '../functions/index.mjs';
|
|
6
|
+
import { replaceNodeWithText } from '../utils/index.mjs';
|
|
6
7
|
import type { TsMorphTransformer } from './types.mjs';
|
|
7
8
|
|
|
8
9
|
const TRANSFORMER_NAME = 'convert-interface-to-type';
|
|
@@ -91,7 +92,7 @@ export const convertInterfaceToTypeTransformer = (): TsMorphTransformer =>
|
|
|
91
92
|
const typeAliasText =
|
|
92
93
|
`${jsDocText}${exportKeyword}type ${interfaceName}${typeParamsStr} = ${mut_typeBody};` as const;
|
|
93
94
|
|
|
94
|
-
interfaceDecl
|
|
95
|
+
replaceNodeWithText(interfaceDecl, typeAliasText);
|
|
95
96
|
};
|
|
96
97
|
|
|
97
98
|
const buildTypeLiteral = (
|
|
@@ -29,7 +29,10 @@ import {
|
|
|
29
29
|
wrapWithParentheses,
|
|
30
30
|
type ReadonlyTypeReferenceNode,
|
|
31
31
|
} from '../functions/index.mjs';
|
|
32
|
-
import {
|
|
32
|
+
import {
|
|
33
|
+
replaceNodeWithDebugPrint,
|
|
34
|
+
replaceNodeWithText,
|
|
35
|
+
} from '../utils/index.mjs';
|
|
33
36
|
import {
|
|
34
37
|
groupUnionIntersectionTypes,
|
|
35
38
|
invalidDeepReadonlyTypeName,
|
|
@@ -72,7 +75,7 @@ export const convertToReadonlyTransformer = (
|
|
|
72
75
|
replaceNode:
|
|
73
76
|
options?.debug === true
|
|
74
77
|
? replaceNodeWithDebugPrint
|
|
75
|
-
: (node, newNodeText) => node
|
|
78
|
+
: (node, newNodeText) => replaceNodeWithText(node, newNodeText),
|
|
76
79
|
} as const;
|
|
77
80
|
|
|
78
81
|
return {
|
|
@@ -7,9 +7,7 @@ import * as prettierPluginEstree from 'prettier/plugins/estree';
|
|
|
7
7
|
import * as prettierPluginTypeScript from 'prettier/plugins/typescript';
|
|
8
8
|
import * as prettier from 'prettier/standalone';
|
|
9
9
|
import { expectType } from 'ts-data-forge';
|
|
10
|
-
|
|
11
|
-
// rewrite `describe('DeepReadonly', ...)` into a reference to this type.
|
|
12
|
-
import type { DeepReadonly as TsTypeForgeDeepReadonly } from 'ts-type-forge';
|
|
10
|
+
import type { DeepReadonly } from 'ts-type-forge';
|
|
13
11
|
import {
|
|
14
12
|
convertToReadonlyTransformer,
|
|
15
13
|
type ReadonlyTransformerOptions,
|
|
@@ -3090,34 +3088,25 @@ describe(convertToReadonlyTransformer, () => {
|
|
|
3090
3088
|
type B = Readonly<{ b: string[] }>;
|
|
3091
3089
|
|
|
3092
3090
|
// `|` commutes with DeepReadonly, so merging a union is safe.
|
|
3093
|
-
expectType<
|
|
3094
|
-
TsTypeForgeDeepReadonly<A | B>,
|
|
3095
|
-
TsTypeForgeDeepReadonly<A> | TsTypeForgeDeepReadonly<B>
|
|
3096
|
-
>('=');
|
|
3091
|
+
expectType<DeepReadonly<A | B>, DeepReadonly<A> | DeepReadonly<B>>('=');
|
|
3097
3092
|
|
|
3098
|
-
expectType<
|
|
3099
|
-
TsTypeForgeDeepReadonly<F | X>,
|
|
3100
|
-
TsTypeForgeDeepReadonly<F> | TsTypeForgeDeepReadonly<X>
|
|
3101
|
-
>('=');
|
|
3093
|
+
expectType<DeepReadonly<F | X>, DeepReadonly<F> | DeepReadonly<X>>('=');
|
|
3102
3094
|
|
|
3103
3095
|
expectType<
|
|
3104
|
-
|
|
3105
|
-
|
|
3096
|
+
DeepReadonly<readonly string[] | X>,
|
|
3097
|
+
DeepReadonly<readonly string[]> | DeepReadonly<X>
|
|
3106
3098
|
>('=');
|
|
3107
3099
|
|
|
3108
3100
|
// `&` does not: an intersection is tested as a whole and matches every
|
|
3109
3101
|
// branch any one member matches. `F & X` hits the function branch and
|
|
3110
3102
|
// comes back unchanged, leaving `x` mutable.
|
|
3111
|
-
expectType<
|
|
3103
|
+
expectType<DeepReadonly<F & X>, F & X>('=');
|
|
3112
3104
|
|
|
3113
|
-
expectType<
|
|
3114
|
-
TsTypeForgeDeepReadonly<F & X>,
|
|
3115
|
-
TsTypeForgeDeepReadonly<F> & TsTypeForgeDeepReadonly<X>
|
|
3116
|
-
>('!=');
|
|
3105
|
+
expectType<DeepReadonly<F & X>, DeepReadonly<F> & DeepReadonly<X>>('!=');
|
|
3117
3106
|
|
|
3118
3107
|
expectType<
|
|
3119
|
-
|
|
3120
|
-
|
|
3108
|
+
DeepReadonly<readonly string[] & X>,
|
|
3109
|
+
DeepReadonly<readonly string[]> & DeepReadonly<X>
|
|
3121
3110
|
>('!=');
|
|
3122
3111
|
|
|
3123
3112
|
// `Readonly` over type literals does commute with `&`, which is what
|