ts-codemod-lib 2.2.2 → 2.2.4
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/README.md +0 -12
- package/dist/cmd/append-as-const.mjs +1 -1
- package/dist/cmd/append-as-const.mjs.map +1 -1
- package/dist/cmd/convert-interface-to-type.mjs +1 -1
- package/dist/cmd/convert-interface-to-type.mjs.map +1 -1
- package/dist/cmd/convert-to-readonly.mjs +1 -1
- package/dist/cmd/convert-to-readonly.mjs.map +1 -1
- package/dist/cmd/replace-any-with-unknown.mjs +1 -1
- package/dist/cmd/replace-any-with-unknown.mjs.map +1 -1
- package/dist/cmd/replace-record-with-unknown-record.mjs +1 -1
- package/dist/cmd/replace-record-with-unknown-record.mjs.map +1 -1
- package/dist/cmd/run-transformer-cli.mjs +1 -1
- package/dist/cmd/run-transformer-cli.mjs.map +1 -1
- package/dist/functions/ast-transformers/append-as-const.mjs +1 -1
- package/dist/functions/ast-transformers/append-as-const.mjs.map +1 -1
- package/dist/functions/ast-transformers/convert-interface-to-type.mjs +4 -4
- package/dist/functions/ast-transformers/convert-interface-to-type.mjs.map +1 -1
- package/dist/functions/ast-transformers/convert-to-readonly.mjs +10 -10
- package/dist/functions/ast-transformers/convert-to-readonly.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 +13 -13
- package/dist/functions/ast-transformers/replace-record-with-unknown-record.mjs.map +1 -1
- package/dist/functions/ast-transformers/transform-source-code.mjs +2 -2
- package/dist/functions/ast-transformers/transform-source-code.mjs.map +1 -1
- package/dist/functions/functions/is-as-const-node.mjs +1 -1
- package/dist/functions/functions/is-as-const-node.mjs.map +1 -1
- package/dist/functions/functions/is-readonly-node.mjs +1 -1
- package/dist/functions/functions/is-readonly-node.mjs.map +1 -1
- package/package.json +47 -40
- package/src/cmd/append-as-const.mts +1 -1
- package/src/cmd/convert-interface-to-type.mts +1 -1
- package/src/cmd/convert-to-readonly.mts +1 -1
- package/src/cmd/replace-any-with-unknown.mts +1 -1
- package/src/cmd/replace-record-with-unknown-record.mts +1 -1
- package/src/cmd/run-transformer-cli.mts +1 -1
- package/src/functions/ast-transformers/append-as-const.mts +1 -1
- package/src/functions/ast-transformers/convert-interface-to-type.mts +4 -4
- package/src/functions/ast-transformers/convert-to-readonly.mts +11 -16
- package/src/functions/ast-transformers/replace-record-with-unknown-record.mts +18 -29
- package/src/functions/ast-transformers/transform-source-code.mts +2 -2
- package/src/functions/functions/is-as-const-node.mts +1 -1
- package/src/functions/functions/is-readonly-node.mts +1 -1
|
@@ -16,7 +16,7 @@ const isAsConstNode = (node) => {
|
|
|
16
16
|
if (!typeNameNode.isKind(tsm.SyntaxKind.Identifier)) {
|
|
17
17
|
return false;
|
|
18
18
|
}
|
|
19
|
-
return typeNameNode.getText() === "const" && Arr.
|
|
19
|
+
return typeNameNode.getText() === "const" && Arr.isEmpty(typeNode.getTypeArguments());
|
|
20
20
|
};
|
|
21
21
|
|
|
22
22
|
export { isAsConstNode };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"is-as-const-node.mjs","sources":["../../../src/functions/functions/is-as-const-node.mts"],"sourcesContent":["import { Arr } from 'ts-data-forge';\nimport * as ts from 'ts-morph';\n\nexport const isAsConstNode = (\n node: ts.Node,\n): node is ts.AsExpression &\n Readonly<{\n type: ts.TypeReferenceNode &\n Readonly<{\n typeName: ts.Identifier &\n Readonly<{\n text: 'const';\n }>;\n typeArguments: undefined;\n }>;\n }> => {\n if (!node.isKind(ts.SyntaxKind.AsExpression)) {\n return false;\n }\n\n // 2. Get the 'type' node from the AsExpression\n const typeNode = node.getTypeNode();\n\n if (typeNode === undefined) {\n return false; // Should have a type node for 'as const'\n }\n\n // 3. Check if the 'type' node is a TypeReference\n if (!typeNode.isKind(ts.SyntaxKind.TypeReference)) {\n return false;\n }\n\n // 4. Get the 'typeName' from the TypeReference\n const typeNameNode = typeNode.getTypeName();\n\n // 5. Check if the 'typeName' is an Identifier\n if (!typeNameNode.isKind(ts.SyntaxKind.Identifier)) {\n // 'as const' uses a simple Identifier 'const', not a QualifiedName\n return false;\n }\n\n // 6. Check if the Identifier's text is 'const'\n // and that there are no type arguments (as const doesn't have them)\n return (\n typeNameNode.getText() === 'const' &&\n Arr.
|
|
1
|
+
{"version":3,"file":"is-as-const-node.mjs","sources":["../../../src/functions/functions/is-as-const-node.mts"],"sourcesContent":["import { Arr } from 'ts-data-forge';\nimport * as ts from 'ts-morph';\n\nexport const isAsConstNode = (\n node: ts.Node,\n): node is ts.AsExpression &\n Readonly<{\n type: ts.TypeReferenceNode &\n Readonly<{\n typeName: ts.Identifier &\n Readonly<{\n text: 'const';\n }>;\n typeArguments: undefined;\n }>;\n }> => {\n if (!node.isKind(ts.SyntaxKind.AsExpression)) {\n return false;\n }\n\n // 2. Get the 'type' node from the AsExpression\n const typeNode = node.getTypeNode();\n\n if (typeNode === undefined) {\n return false; // Should have a type node for 'as const'\n }\n\n // 3. Check if the 'type' node is a TypeReference\n if (!typeNode.isKind(ts.SyntaxKind.TypeReference)) {\n return false;\n }\n\n // 4. Get the 'typeName' from the TypeReference\n const typeNameNode = typeNode.getTypeName();\n\n // 5. Check if the 'typeName' is an Identifier\n if (!typeNameNode.isKind(ts.SyntaxKind.Identifier)) {\n // 'as const' uses a simple Identifier 'const', not a QualifiedName\n return false;\n }\n\n // 6. Check if the Identifier's text is 'const'\n // and that there are no type arguments (as const doesn't have them)\n return (\n typeNameNode.getText() === 'const' &&\n Arr.isEmpty(typeNode.getTypeArguments())\n );\n};\n"],"names":["ts"],"mappings":";;;AAGO,MAAM,aAAA,GAAgB,CAC3B,IAAA,KAWM;AACN,EAAA,IAAI,CAAC,IAAA,CAAK,MAAA,CAAOA,GAAA,CAAG,UAAA,CAAW,YAAY,CAAA,EAAG;AAC5C,IAAA,OAAO,KAAA;AAAA,EACT;AAGA,EAAA,MAAM,QAAA,GAAW,KAAK,WAAA,EAAY;AAElC,EAAA,IAAI,aAAa,MAAA,EAAW;AAC1B,IAAA,OAAO,KAAA;AAAA,EACT;AAGA,EAAA,IAAI,CAAC,QAAA,CAAS,MAAA,CAAOA,GAAA,CAAG,UAAA,CAAW,aAAa,CAAA,EAAG;AACjD,IAAA,OAAO,KAAA;AAAA,EACT;AAGA,EAAA,MAAM,YAAA,GAAe,SAAS,WAAA,EAAY;AAG1C,EAAA,IAAI,CAAC,YAAA,CAAa,MAAA,CAAOA,GAAA,CAAG,UAAA,CAAW,UAAU,CAAA,EAAG;AAElD,IAAA,OAAO,KAAA;AAAA,EACT;AAIA,EAAA,OACE,YAAA,CAAa,SAAQ,KAAM,OAAA,IAC3B,IAAI,OAAA,CAAQ,QAAA,CAAS,kBAAkB,CAAA;AAE3C;;;;"}
|
|
@@ -13,7 +13,7 @@ const isReadonlyTypeReferenceNode = (node) => {
|
|
|
13
13
|
}
|
|
14
14
|
const typeName = node.getTypeName();
|
|
15
15
|
const typeArguments = node.getTypeArguments();
|
|
16
|
-
return typeName.isKind(tsm.SyntaxKind.Identifier) && typeName.getText() === "Readonly" && Arr.
|
|
16
|
+
return typeName.isKind(tsm.SyntaxKind.Identifier) && typeName.getText() === "Readonly" && Arr.isFixedLengthArray(1, typeArguments);
|
|
17
17
|
};
|
|
18
18
|
|
|
19
19
|
export { isReadonlyArrayTypeNode, isReadonlyTupleOrArrayTypeNode, isReadonlyTupleTypeNode, isReadonlyTypeReferenceNode, isShallowReadonlyTypeNode };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"is-readonly-node.mjs","sources":["../../../src/functions/functions/is-readonly-node.mts"],"sourcesContent":["import { Arr, expectType } from 'ts-data-forge';\nimport * as tsm from 'ts-morph';\nimport { type DeepReadonly } from 'ts-type-forge';\nimport { isAtomicTypeNode } from './is-atomic-type-node.mjs';\n\nexport const isShallowReadonlyTypeNode = (node: tsm.Node): boolean =>\n isReadonlyTupleOrArrayTypeNode(node) ||\n isReadonlyTypeReferenceNode(node) ||\n isAtomicTypeNode(node);\n\nexport const isReadonlyTupleOrArrayTypeNode = (\n node: DeepReadonly<tsm.Node>,\n): node is ReadonlyArrayTypeNode | ReadonlyTupleTypeNode =>\n node.isKind(tsm.SyntaxKind.TypeOperator) &&\n node.getOperator() === tsm.SyntaxKind.ReadonlyKeyword &&\n (node.getTypeNode().isKind(tsm.SyntaxKind.ArrayType) || // Use optional chaining and isKind\n node.getTypeNode().isKind(tsm.SyntaxKind.TupleType));\n\nexport type ReadonlyArrayTypeNode = tsm.TypeNode &\n Omit<tsm.TypeOperatorTypeNode, 'getOperator' | 'getTypeNode'> &\n Readonly<{\n getOperator: () => tsm.SyntaxKind.ReadonlyKeyword;\n getTypeNode: () => tsm.ArrayTypeNode;\n }>;\n\nexport const isReadonlyArrayTypeNode = (\n node: DeepReadonly<tsm.Node>,\n): node is ReadonlyArrayTypeNode =>\n node.isKind(tsm.SyntaxKind.TypeOperator) &&\n node.getOperator() === tsm.SyntaxKind.ReadonlyKeyword &&\n node.getTypeNode().isKind(tsm.SyntaxKind.ArrayType);\n\n// Helper to get a specific type node from source code\nconst getTypeNodeFromSource = (\n sourceCode: string,\n typeName: string,\n): tsm.Node => {\n const project = new tsm.Project({ useInMemoryFileSystem: true });\n\n const sourceFile = project.createSourceFile('test.ts', sourceCode);\n\n const typeAlias = sourceFile.getTypeAliasOrThrow(typeName);\n\n return typeAlias.getTypeNodeOrThrow();\n};\n\nif (import.meta.vitest !== undefined) {\n describe('isReadonlyArrayTypeNode', () => {\n test('should return true for readonly array type', () => {\n const node = getTypeNodeFromSource(\n 'type Test = readonly number[];',\n 'Test',\n );\n\n if (isReadonlyArrayTypeNode(node)) {\n const _operator = node.getOperator();\n\n expectType<typeof _operator, tsm.SyntaxKind.ReadonlyKeyword>('=');\n\n const _typeNode = node.getTypeNode();\n\n expectType<typeof _typeNode, tsm.ArrayTypeNode>('=');\n }\n\n assert.isTrue(node.isKind(tsm.SyntaxKind.TypeOperator));\n\n if (!isReadonlyArrayTypeNode(node)) {\n throw new Error('node should be ReadonlyArrayTypeNode');\n }\n\n expect(node.getOperator()).toBe(tsm.SyntaxKind.ReadonlyKeyword);\n\n assert.isTrue(node.getTypeNode().isKind(tsm.SyntaxKind.ArrayType));\n });\n\n test('should return false for non-readonly array', () => {\n const node = getTypeNodeFromSource('type Test = string[];', 'Test');\n\n assert.isFalse(isReadonlyArrayTypeNode(node));\n });\n\n test('should return false for readonly tuple', () => {\n const node = getTypeNodeFromSource(\n 'type Test = readonly [string];',\n 'Test',\n );\n\n assert.isFalse(isReadonlyArrayTypeNode(node));\n });\n });\n}\n\nexport type ReadonlyTupleTypeNode = tsm.TypeNode &\n Omit<tsm.TypeOperatorTypeNode, 'getOperator' | 'getTypeNode'> &\n Readonly<{\n getOperator: () => tsm.SyntaxKind.ReadonlyKeyword;\n getTypeNode: () => tsm.TupleTypeNode;\n }>;\n\nexport const isReadonlyTupleTypeNode = (\n node: DeepReadonly<tsm.Node>,\n): node is ReadonlyTupleTypeNode =>\n node.isKind(tsm.SyntaxKind.TypeOperator) &&\n node.getOperator() === tsm.SyntaxKind.ReadonlyKeyword &&\n node.getTypeNode().isKind(tsm.SyntaxKind.TupleType);\n\nif (import.meta.vitest !== undefined) {\n describe('isReadonlyTupleTypeNode', () => {\n test('should return true for readonly tuple type', () => {\n const node = getTypeNodeFromSource(\n 'type Test = readonly [number, string];',\n 'Test',\n );\n\n assert.isTrue(isReadonlyTupleTypeNode(node));\n\n if (isReadonlyTupleTypeNode(node)) {\n const _operator = node.getOperator();\n\n expectType<typeof _operator, tsm.SyntaxKind.ReadonlyKeyword>('=');\n\n const _typeNode = node.getTypeNode();\n\n expectType<typeof _typeNode, tsm.TupleTypeNode>('=');\n }\n\n assert.isTrue(node.isKind(tsm.SyntaxKind.TypeOperator));\n\n if (!isReadonlyTupleTypeNode(node)) {\n throw new Error('node should be ReadonlyTupleTypeNode');\n }\n\n expect(node.getOperator()).toBe(tsm.SyntaxKind.ReadonlyKeyword);\n\n assert.isTrue(node.getTypeNode().isKind(tsm.SyntaxKind.TupleType));\n });\n\n test('should return false for non-readonly tuple', () => {\n const node = getTypeNodeFromSource('type Test = [boolean];', 'Test');\n\n assert.isFalse(isReadonlyTupleTypeNode(node));\n });\n\n test('should return false for readonly array', () => {\n const node = getTypeNodeFromSource(\n 'type Test = readonly number[];',\n 'Test',\n );\n\n assert.isFalse(isReadonlyTupleTypeNode(node));\n });\n });\n}\n\nexport type ReadonlyTypeReferenceNode = tsm.TypeNode &\n Omit<tsm.TypeReferenceNode, 'getTypeName' | 'getTypeArguments'> &\n Readonly<{\n getTypeName: () => Omit<tsm.Identifier, 'getText'> &\n Readonly<{\n getText: () => 'Readonly';\n }>;\n getTypeArguments: () => readonly [tsm.TypeNode];\n }>;\n\nexport const isReadonlyTypeReferenceNode = (\n node: DeepReadonly<tsm.Node>,\n): node is ReadonlyTypeReferenceNode => {\n if (!node.isKind(tsm.SyntaxKind.TypeReference)) {\n return false;\n }\n\n const typeName = node.getTypeName();\n\n const typeArguments = node.getTypeArguments();\n\n return (\n typeName.isKind(tsm.SyntaxKind.Identifier) &&\n typeName.getText() === 'Readonly' &&\n Arr.
|
|
1
|
+
{"version":3,"file":"is-readonly-node.mjs","sources":["../../../src/functions/functions/is-readonly-node.mts"],"sourcesContent":["import { Arr, expectType } from 'ts-data-forge';\nimport * as tsm from 'ts-morph';\nimport { type DeepReadonly } from 'ts-type-forge';\nimport { isAtomicTypeNode } from './is-atomic-type-node.mjs';\n\nexport const isShallowReadonlyTypeNode = (node: tsm.Node): boolean =>\n isReadonlyTupleOrArrayTypeNode(node) ||\n isReadonlyTypeReferenceNode(node) ||\n isAtomicTypeNode(node);\n\nexport const isReadonlyTupleOrArrayTypeNode = (\n node: DeepReadonly<tsm.Node>,\n): node is ReadonlyArrayTypeNode | ReadonlyTupleTypeNode =>\n node.isKind(tsm.SyntaxKind.TypeOperator) &&\n node.getOperator() === tsm.SyntaxKind.ReadonlyKeyword &&\n (node.getTypeNode().isKind(tsm.SyntaxKind.ArrayType) || // Use optional chaining and isKind\n node.getTypeNode().isKind(tsm.SyntaxKind.TupleType));\n\nexport type ReadonlyArrayTypeNode = tsm.TypeNode &\n Omit<tsm.TypeOperatorTypeNode, 'getOperator' | 'getTypeNode'> &\n Readonly<{\n getOperator: () => tsm.SyntaxKind.ReadonlyKeyword;\n getTypeNode: () => tsm.ArrayTypeNode;\n }>;\n\nexport const isReadonlyArrayTypeNode = (\n node: DeepReadonly<tsm.Node>,\n): node is ReadonlyArrayTypeNode =>\n node.isKind(tsm.SyntaxKind.TypeOperator) &&\n node.getOperator() === tsm.SyntaxKind.ReadonlyKeyword &&\n node.getTypeNode().isKind(tsm.SyntaxKind.ArrayType);\n\n// Helper to get a specific type node from source code\nconst getTypeNodeFromSource = (\n sourceCode: string,\n typeName: string,\n): tsm.Node => {\n const project = new tsm.Project({ useInMemoryFileSystem: true });\n\n const sourceFile = project.createSourceFile('test.ts', sourceCode);\n\n const typeAlias = sourceFile.getTypeAliasOrThrow(typeName);\n\n return typeAlias.getTypeNodeOrThrow();\n};\n\nif (import.meta.vitest !== undefined) {\n describe('isReadonlyArrayTypeNode', () => {\n test('should return true for readonly array type', () => {\n const node = getTypeNodeFromSource(\n 'type Test = readonly number[];',\n 'Test',\n );\n\n if (isReadonlyArrayTypeNode(node)) {\n const _operator = node.getOperator();\n\n expectType<typeof _operator, tsm.SyntaxKind.ReadonlyKeyword>('=');\n\n const _typeNode = node.getTypeNode();\n\n expectType<typeof _typeNode, tsm.ArrayTypeNode>('=');\n }\n\n assert.isTrue(node.isKind(tsm.SyntaxKind.TypeOperator));\n\n if (!isReadonlyArrayTypeNode(node)) {\n throw new Error('node should be ReadonlyArrayTypeNode');\n }\n\n expect(node.getOperator()).toBe(tsm.SyntaxKind.ReadonlyKeyword);\n\n assert.isTrue(node.getTypeNode().isKind(tsm.SyntaxKind.ArrayType));\n });\n\n test('should return false for non-readonly array', () => {\n const node = getTypeNodeFromSource('type Test = string[];', 'Test');\n\n assert.isFalse(isReadonlyArrayTypeNode(node));\n });\n\n test('should return false for readonly tuple', () => {\n const node = getTypeNodeFromSource(\n 'type Test = readonly [string];',\n 'Test',\n );\n\n assert.isFalse(isReadonlyArrayTypeNode(node));\n });\n });\n}\n\nexport type ReadonlyTupleTypeNode = tsm.TypeNode &\n Omit<tsm.TypeOperatorTypeNode, 'getOperator' | 'getTypeNode'> &\n Readonly<{\n getOperator: () => tsm.SyntaxKind.ReadonlyKeyword;\n getTypeNode: () => tsm.TupleTypeNode;\n }>;\n\nexport const isReadonlyTupleTypeNode = (\n node: DeepReadonly<tsm.Node>,\n): node is ReadonlyTupleTypeNode =>\n node.isKind(tsm.SyntaxKind.TypeOperator) &&\n node.getOperator() === tsm.SyntaxKind.ReadonlyKeyword &&\n node.getTypeNode().isKind(tsm.SyntaxKind.TupleType);\n\nif (import.meta.vitest !== undefined) {\n describe('isReadonlyTupleTypeNode', () => {\n test('should return true for readonly tuple type', () => {\n const node = getTypeNodeFromSource(\n 'type Test = readonly [number, string];',\n 'Test',\n );\n\n assert.isTrue(isReadonlyTupleTypeNode(node));\n\n if (isReadonlyTupleTypeNode(node)) {\n const _operator = node.getOperator();\n\n expectType<typeof _operator, tsm.SyntaxKind.ReadonlyKeyword>('=');\n\n const _typeNode = node.getTypeNode();\n\n expectType<typeof _typeNode, tsm.TupleTypeNode>('=');\n }\n\n assert.isTrue(node.isKind(tsm.SyntaxKind.TypeOperator));\n\n if (!isReadonlyTupleTypeNode(node)) {\n throw new Error('node should be ReadonlyTupleTypeNode');\n }\n\n expect(node.getOperator()).toBe(tsm.SyntaxKind.ReadonlyKeyword);\n\n assert.isTrue(node.getTypeNode().isKind(tsm.SyntaxKind.TupleType));\n });\n\n test('should return false for non-readonly tuple', () => {\n const node = getTypeNodeFromSource('type Test = [boolean];', 'Test');\n\n assert.isFalse(isReadonlyTupleTypeNode(node));\n });\n\n test('should return false for readonly array', () => {\n const node = getTypeNodeFromSource(\n 'type Test = readonly number[];',\n 'Test',\n );\n\n assert.isFalse(isReadonlyTupleTypeNode(node));\n });\n });\n}\n\nexport type ReadonlyTypeReferenceNode = tsm.TypeNode &\n Omit<tsm.TypeReferenceNode, 'getTypeName' | 'getTypeArguments'> &\n Readonly<{\n getTypeName: () => Omit<tsm.Identifier, 'getText'> &\n Readonly<{\n getText: () => 'Readonly';\n }>;\n getTypeArguments: () => readonly [tsm.TypeNode];\n }>;\n\nexport const isReadonlyTypeReferenceNode = (\n node: DeepReadonly<tsm.Node>,\n): node is ReadonlyTypeReferenceNode => {\n if (!node.isKind(tsm.SyntaxKind.TypeReference)) {\n return false;\n }\n\n const typeName = node.getTypeName();\n\n const typeArguments = node.getTypeArguments();\n\n return (\n typeName.isKind(tsm.SyntaxKind.Identifier) &&\n typeName.getText() === 'Readonly' &&\n Arr.isFixedLengthArray(1, typeArguments)\n );\n};\n\nif (import.meta.vitest !== undefined) {\n describe('isReadonlyTypeReferenceNode', () => {\n test('should return true for Readonly<T>', () => {\n const node = getTypeNodeFromSource(\n 'type Test = Readonly<{ x: number }>;',\n 'Test',\n );\n\n assert.isTrue(isReadonlyTypeReferenceNode(node));\n\n if (isReadonlyTypeReferenceNode(node)) {\n const _typeNameText = node.getTypeName().getText();\n\n expectType<typeof _typeNameText, 'Readonly'>('=');\n\n const _typeArguments = node.getTypeArguments();\n\n expectType<\n typeof _typeArguments,\n readonly [tsm.TypeNode<tsm.ts.TypeNode>]\n >('=');\n }\n\n assert.isTrue(node.isKind(tsm.SyntaxKind.TypeReference));\n\n if (!isReadonlyTypeReferenceNode(node)) {\n throw new Error('node should be ReadonlyNode');\n }\n\n expect(node.getTypeName().getText()).toBe('Readonly');\n\n expect(node.getTypeArguments()).toHaveLength(1);\n });\n\n test('should return false for other type references', () => {\n const node = getTypeNodeFromSource(\n 'type Test = Partial<{ y: string }>;',\n 'Test',\n );\n\n assert.isFalse(isReadonlyTypeReferenceNode(node));\n });\n\n test('should return false for Readonly without type arguments', () => {\n // Note: This is syntactically incorrect TS, but testing the guard\n const node = getTypeNodeFromSource('type Test = Readonly;', 'Test');\n\n assert.isFalse(isReadonlyTypeReferenceNode(node));\n });\n\n test('should return false for Readonly with multiple type arguments', () => {\n // Note: This is syntactically incorrect TS, but testing the guard\n const project = new tsm.Project({ useInMemoryFileSystem: true });\n\n // Need to create manually as TS parser might reject Readonly<A, B>\n const sourceFile = project.createSourceFile(\n 'test.ts',\n 'type Test = Readonly<string, number>;',\n );\n\n const node = sourceFile.getTypeAliasOrThrow('Test').getTypeNodeOrThrow();\n\n assert.isFalse(isReadonlyTypeReferenceNode(node));\n });\n });\n}\n"],"names":[],"mappings":";;;;MAKa,yBAAA,GAA4B,CAAC,IAAA,KACxC,8BAAA,CAA+B,IAAI,KACnC,2BAAA,CAA4B,IAAI,CAAA,IAChC,gBAAA,CAAiB,IAAI;MAEV,8BAAA,GAAiC,CAC5C,IAAA,KAEA,IAAA,CAAK,OAAO,GAAA,CAAI,UAAA,CAAW,YAAY,CAAA,IACvC,IAAA,CAAK,aAAY,KAAM,GAAA,CAAI,WAAW,eAAA,KACrC,IAAA,CAAK,aAAY,CAAE,MAAA,CAAO,GAAA,CAAI,UAAA,CAAW,SAAS,CAAA;AACjD,IAAA,CAAK,WAAA,EAAY,CAAE,MAAA,CAAO,GAAA,CAAI,WAAW,SAAS,CAAA;MASzC,uBAAA,GAA0B,CACrC,IAAA,KAEA,IAAA,CAAK,OAAO,GAAA,CAAI,UAAA,CAAW,YAAY,CAAA,IACvC,IAAA,CAAK,aAAY,KAAM,GAAA,CAAI,WAAW,eAAA,IACtC,IAAA,CAAK,aAAY,CAAE,MAAA,CAAO,GAAA,CAAI,UAAA,CAAW,SAAS;MAqEvC,uBAAA,GAA0B,CACrC,IAAA,KAEA,IAAA,CAAK,OAAO,GAAA,CAAI,UAAA,CAAW,YAAY,CAAA,IACvC,IAAA,CAAK,aAAY,KAAM,GAAA,CAAI,WAAW,eAAA,IACtC,IAAA,CAAK,aAAY,CAAE,MAAA,CAAO,GAAA,CAAI,UAAA,CAAW,SAAS;AA4D7C,MAAM,2BAAA,GAA8B,CACzC,IAAA,KACsC;AACtC,EAAA,IAAI,CAAC,IAAA,CAAK,MAAA,CAAO,GAAA,CAAI,UAAA,CAAW,aAAa,CAAA,EAAG;AAC9C,IAAA,OAAO,KAAA;AAAA,EAAA;AAGT,EAAA,MAAM,QAAA,GAAW,KAAK,WAAA,EAAY;AAElC,EAAA,MAAM,aAAA,GAAgB,KAAK,gBAAA,EAAiB;AAE5C,EAAA,OACE,QAAA,CAAS,MAAA,CAAO,GAAA,CAAI,UAAA,CAAW,UAAU,CAAA,IACzC,QAAA,CAAS,OAAA,EAAQ,KAAM,UAAA,IACvB,GAAA,CAAI,kBAAA,CAAmB,GAAG,aAAa,CAAA;AAE3C;;;;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ts-codemod-lib",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.4",
|
|
4
4
|
"private": false,
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typescript",
|
|
@@ -24,6 +24,10 @@
|
|
|
24
24
|
"import": {
|
|
25
25
|
"types": "./dist/types.d.mts",
|
|
26
26
|
"default": "./dist/entry-point.mjs"
|
|
27
|
+
},
|
|
28
|
+
"default": {
|
|
29
|
+
"types": "./dist/types.d.mts",
|
|
30
|
+
"default": "./dist/entry-point.mjs"
|
|
27
31
|
}
|
|
28
32
|
}
|
|
29
33
|
},
|
|
@@ -43,31 +47,33 @@
|
|
|
43
47
|
"LICENSE"
|
|
44
48
|
],
|
|
45
49
|
"scripts": {
|
|
50
|
+
"agents:gen": "tsx ./scripts/cmd/gen-agents-md.mts",
|
|
51
|
+
"agents:sync": "tsx ./scripts/cmd/sync-agent-config.mts",
|
|
46
52
|
"build": "tsx ./scripts/cmd/build.mts",
|
|
47
53
|
"build:min": "tsx ./scripts/cmd/build.mts --skip-check",
|
|
48
|
-
"check:ext": "tsx ./scripts/cmd/check-ext.mts",
|
|
49
54
|
"check-all": "tsx ./scripts/cmd/check-all.mts",
|
|
55
|
+
"check:ext": "tsx ./scripts/cmd/check-ext.mts",
|
|
50
56
|
"clean": "pnpm run /clean:.*/",
|
|
51
57
|
"clean:logs": "npx rimraf ./*.log",
|
|
52
58
|
"codemod": "run-s codemod:uncommitted:* fmt",
|
|
53
59
|
"codemod:diff": "run-s codemod:diff:* fmt",
|
|
54
|
-
"codemod:diff:as-const": "tsx ./src/cmd/append-as-const.mts --diff-from origin/main '{src,scripts,samples,test}/**/*.{mts,tsx}'",
|
|
55
60
|
"codemod:diff:any-to-unknown": "tsx ./src/cmd/replace-any-with-unknown.mts --diff-from origin/main '{src,scripts,samples,test}/**/*.{mts,tsx}'",
|
|
56
|
-
"codemod:diff:
|
|
61
|
+
"codemod:diff:as-const": "tsx ./src/cmd/append-as-const.mts --diff-from origin/main '{src,scripts,samples,test}/**/*.{mts,tsx}'",
|
|
57
62
|
"codemod:diff:interface-to-type": "tsx ./src/cmd/convert-interface-to-type.mts --diff-from origin/main '{src,scripts,samples,test}/**/*.{mts,tsx}'",
|
|
58
63
|
"codemod:diff:readonly": "tsx ./src/cmd/convert-to-readonly.mts --diff-from origin/main '{src,scripts,samples,test}/**/*.{mts,tsx}'",
|
|
64
|
+
"codemod:diff:unknown-record": "tsx ./src/cmd/replace-record-with-unknown-record.mts --diff-from origin/main '{src,scripts,samples,test}/**/*.{mts,tsx}'",
|
|
59
65
|
"codemod:full": "run-s codemod:full:* fmt",
|
|
60
|
-
"codemod:full:as-const": "tsx ./src/cmd/append-as-const.mts '{src,scripts,samples,test}/**/*.{mts,tsx}'",
|
|
61
66
|
"codemod:full:any-to-unknown": "tsx ./src/cmd/replace-any-with-unknown.mts '{src,scripts,samples,test}/**/*.{mts,tsx}'",
|
|
62
|
-
"codemod:full:
|
|
67
|
+
"codemod:full:as-const": "tsx ./src/cmd/append-as-const.mts '{src,scripts,samples,test}/**/*.{mts,tsx}'",
|
|
63
68
|
"codemod:full:interface-to-type": "tsx ./src/cmd/convert-interface-to-type.mts '{src,scripts,samples,test}/**/*.{mts,tsx}'",
|
|
64
69
|
"codemod:full:readonly": "tsx ./src/cmd/convert-to-readonly.mts '{src,scripts,samples,test}/**/*.{mts,tsx}'",
|
|
65
|
-
"codemod:
|
|
70
|
+
"codemod:full:unknown-record": "tsx ./src/cmd/replace-record-with-unknown-record.mts '{src,scripts,samples,test}/**/*.{mts,tsx}'",
|
|
66
71
|
"codemod:uncommitted:any-to-unknown": "tsx ./src/cmd/replace-any-with-unknown.mts --uncommitted '{src,scripts,samples,test}/**/*.{mts,tsx}'",
|
|
67
|
-
"codemod:uncommitted:
|
|
72
|
+
"codemod:uncommitted:as-const": "tsx ./src/cmd/append-as-const.mts --uncommitted '{src,scripts,samples,test}/**/*.{mts,tsx}'",
|
|
68
73
|
"codemod:uncommitted:interface-to-type": "tsx ./src/cmd/convert-interface-to-type.mts --uncommitted '{src,scripts,samples,test}/**/*.{mts,tsx}'",
|
|
69
74
|
"codemod:uncommitted:readonly": "tsx ./src/cmd/convert-to-readonly.mts --uncommitted '{src,scripts,samples,test}/**/*.{mts,tsx}'",
|
|
70
|
-
"
|
|
75
|
+
"codemod:uncommitted:unknown-record": "tsx ./src/cmd/replace-record-with-unknown-record.mts --uncommitted '{src,scripts,samples,test}/**/*.{mts,tsx}'",
|
|
76
|
+
"cspell": "cspell \"{**,.changeset/**/*,.github/**/*}\" --gitignore --gitignore-root ./ --no-progress",
|
|
71
77
|
"doc": "tsx ./scripts/cmd/gen-docs.mts",
|
|
72
78
|
"doc:embed": "tsx ./scripts/cmd/embed-examples.mts",
|
|
73
79
|
"doc:preview": "vite preview --config ./configs/vite.doc.config.mts",
|
|
@@ -75,19 +81,14 @@
|
|
|
75
81
|
"fmt": "format-uncommitted",
|
|
76
82
|
"fmt:diff": "format-diff-from origin/main",
|
|
77
83
|
"fmt:full": "prettier --write .",
|
|
78
|
-
"gh:apply-all": "gh-apply-all",
|
|
79
|
-
"gh:apply-repository-settings": "gh-apply-repository-settings",
|
|
80
|
-
"gh:apply-rulesets": "gh-apply-rulesets",
|
|
81
|
-
"gh:apply-variables": "gh-apply-variables",
|
|
82
|
-
"gh:backup-all": "gh-backup-all",
|
|
83
|
-
"gh:backup-repository-settings": "gh-backup-repository-settings",
|
|
84
|
-
"gh:backup-rulesets": "gh-backup-rulesets",
|
|
85
84
|
"gi": "run-s gi:src fmt",
|
|
86
85
|
"gi:src": "gen-index-ts ./src --index-ext .mts --export-ext .mjs --target-ext .mts --target-ext .tsx --exclude entry-point.mts --exclude cmd",
|
|
87
86
|
"lint": "eslint .",
|
|
88
87
|
"lint:fix": "eslint . --fix",
|
|
89
88
|
"md": "markdownlint-cli2",
|
|
90
89
|
"prepare-release": "run-s sync-cli-versions fmt build doc",
|
|
90
|
+
"repo-settings:apply": "repo-settings apply",
|
|
91
|
+
"repo-settings:backup": "repo-settings backup",
|
|
91
92
|
"run:cmd": "tsx ./src/cmd/convert-to-readonly.mts ./test/sample1.mts",
|
|
92
93
|
"run:test": "tsx ./scripts/test-code/transform-test-code.mts",
|
|
93
94
|
"sync-cli-versions": "tsx scripts/cmd/sync-cli-versions.mts",
|
|
@@ -100,56 +101,58 @@
|
|
|
100
101
|
"tsc": "node ./node_modules/typescript-native/bin/tsc --noEmit",
|
|
101
102
|
"tscw": "node ./node_modules/typescript-native/bin/tsc --noEmit --watch -p ./tsconfig.json",
|
|
102
103
|
"type-check": "node ./node_modules/typescript-native/bin/tsc --noEmit",
|
|
103
|
-
"update-packages": "pnpm update --latest '!typescript'",
|
|
104
104
|
"z:vitest": "vitest --config ./configs/vitest.config.mts",
|
|
105
105
|
"z:vitest:node": "pnpm run z:vitest --project='Node.js'"
|
|
106
106
|
},
|
|
107
107
|
"dependencies": {
|
|
108
|
-
"ts-data-forge": "^
|
|
108
|
+
"ts-data-forge": "^14.0.0",
|
|
109
109
|
"ts-morph": "^28.0.0",
|
|
110
|
-
"ts-type-forge": "^
|
|
110
|
+
"ts-type-forge": "^9.1.1"
|
|
111
111
|
},
|
|
112
112
|
"devDependencies": {
|
|
113
113
|
"@rollup/plugin-replace": "6.0.3",
|
|
114
114
|
"@rollup/plugin-strip": "3.0.4",
|
|
115
|
-
"@semantic-release/changelog": "
|
|
115
|
+
"@semantic-release/changelog": "7.0.0",
|
|
116
116
|
"@semantic-release/commit-analyzer": "13.0.1",
|
|
117
117
|
"@semantic-release/exec": "7.1.0",
|
|
118
|
-
"@semantic-release/git": "
|
|
119
|
-
"@semantic-release/github": "12.0.
|
|
118
|
+
"@semantic-release/git": "11.0.1",
|
|
119
|
+
"@semantic-release/github": "12.0.9",
|
|
120
120
|
"@semantic-release/npm": "13.1.5",
|
|
121
121
|
"@semantic-release/release-notes-generator": "14.1.1",
|
|
122
|
-
"@types/node": "26.1.
|
|
122
|
+
"@types/node": "26.1.2",
|
|
123
123
|
"@vitest/browser-playwright": "4.1.10",
|
|
124
124
|
"@vitest/coverage-v8": "4.1.10",
|
|
125
|
-
"@vitest/ui": "4.1.
|
|
125
|
+
"@vitest/ui": "4.1.10",
|
|
126
126
|
"cmd-ts": "0.15.0",
|
|
127
127
|
"conventional-changelog-conventionalcommits": "10.2.1",
|
|
128
128
|
"cspell": "10.0.1",
|
|
129
129
|
"dedent": "1.7.2",
|
|
130
130
|
"esbuild": "0.28.1",
|
|
131
|
-
"eslint": "10.
|
|
132
|
-
"eslint-config-typed": "5.
|
|
133
|
-
"
|
|
131
|
+
"eslint": "10.8.0",
|
|
132
|
+
"eslint-config-typed": "5.8.2",
|
|
133
|
+
"eslint-plugin-ts-data-forge": "0.3.0",
|
|
134
|
+
"eslint-plugin-ts-fortress": "0.4.0",
|
|
135
|
+
"eslint-plugin-ts-type-forge": "0.4.3",
|
|
136
|
+
"github-settings-as-code": "2.0.1",
|
|
134
137
|
"jiti": "2.7.0",
|
|
135
|
-
"markdownlint": "0.41.
|
|
136
|
-
"markdownlint-cli2": "0.23.
|
|
138
|
+
"markdownlint": "0.41.1",
|
|
139
|
+
"markdownlint-cli2": "0.23.2",
|
|
137
140
|
"npm-run-all2": "9.0.2",
|
|
138
|
-
"playwright": "1.
|
|
139
|
-
"prettier": "3.9.
|
|
141
|
+
"playwright": "1.62.0",
|
|
142
|
+
"prettier": "3.9.6",
|
|
140
143
|
"prettier-plugin-organize-imports": "4.3.0",
|
|
141
144
|
"prettier-plugin-packagejson": "3.0.2",
|
|
142
|
-
"rollup": "4.62.
|
|
145
|
+
"rollup": "4.62.3",
|
|
143
146
|
"rollup-plugin-esbuild": "6.2.1",
|
|
144
|
-
"semantic-release": "25.0.
|
|
145
|
-
"ts-repo-utils": "10.1.
|
|
146
|
-
"tsx": "4.23.
|
|
147
|
-
"typedoc": "0.28.
|
|
147
|
+
"semantic-release": "25.0.8",
|
|
148
|
+
"ts-repo-utils": "10.1.6",
|
|
149
|
+
"tsx": "4.23.1",
|
|
150
|
+
"typedoc": "0.28.20",
|
|
148
151
|
"typedoc-github-theme": "0.4.0",
|
|
149
152
|
"typescript": "6.0.3",
|
|
150
|
-
"typescript-native": "npm:typescript@7.0.
|
|
151
|
-
"vite": "8.1.
|
|
152
|
-
"vitest": "4.1.
|
|
153
|
+
"typescript-native": "npm:typescript@7.0.2",
|
|
154
|
+
"vite": "8.1.5",
|
|
155
|
+
"vitest": "4.1.10"
|
|
153
156
|
},
|
|
154
157
|
"peerDependencies": {
|
|
155
158
|
"cmd-ts": "0.14.3",
|
|
@@ -167,12 +170,16 @@
|
|
|
167
170
|
"optional": true
|
|
168
171
|
}
|
|
169
172
|
},
|
|
170
|
-
"packageManager": "pnpm@11.
|
|
173
|
+
"packageManager": "pnpm@11.20.0",
|
|
171
174
|
"engines": {
|
|
172
175
|
"node": ">=22.22.2",
|
|
173
176
|
"pnpm": ">=9.0.0"
|
|
174
177
|
},
|
|
175
178
|
"volta": {
|
|
176
179
|
"node": "26.3.1"
|
|
180
|
+
},
|
|
181
|
+
"publishConfig": {
|
|
182
|
+
"access": "public",
|
|
183
|
+
"provenance": true
|
|
177
184
|
}
|
|
178
185
|
}
|
|
@@ -51,7 +51,7 @@ export const runTransformerCLI = async (
|
|
|
51
51
|
|
|
52
52
|
const files = filesResult.value;
|
|
53
53
|
|
|
54
|
-
if (Arr.
|
|
54
|
+
if (Arr.isEmpty(files)) {
|
|
55
55
|
echoIfNotSilent(
|
|
56
56
|
options.diffFrom !== undefined
|
|
57
57
|
? `No files found in diff from "${options.diffFrom}" matching pattern: ${options.baseDir}`
|
|
@@ -47,18 +47,18 @@ export const convertInterfaceToTypeTransformer = (): TsMorphTransformer =>
|
|
|
47
47
|
|
|
48
48
|
// Build type literal from members
|
|
49
49
|
const mut_typeBody: string = (() => {
|
|
50
|
-
if (Arr.
|
|
50
|
+
if (Arr.isEmpty(extendsExpressions)) {
|
|
51
51
|
// No extends: simple type literal
|
|
52
52
|
return buildTypeLiteral(members);
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
if (Arr.
|
|
55
|
+
if (Arr.isEmpty(members)) {
|
|
56
56
|
// Only extends, no own members: union of extended types
|
|
57
57
|
const extendedTypes = extendsExpressions.map((ext) =>
|
|
58
58
|
ext.getText(),
|
|
59
59
|
);
|
|
60
60
|
|
|
61
|
-
return Arr.
|
|
61
|
+
return Arr.isFixedLengthArray(1, extendedTypes)
|
|
62
62
|
? extendedTypes[0]
|
|
63
63
|
: extendedTypes.join(' & ');
|
|
64
64
|
}
|
|
@@ -97,7 +97,7 @@ export const convertInterfaceToTypeTransformer = (): TsMorphTransformer =>
|
|
|
97
97
|
const buildTypeLiteral = (
|
|
98
98
|
members: readonly tsm.TypeElementTypes[],
|
|
99
99
|
): string => {
|
|
100
|
-
if (Arr.
|
|
100
|
+
if (Arr.isEmpty(members)) {
|
|
101
101
|
return 'Record<string, never>';
|
|
102
102
|
}
|
|
103
103
|
|
|
@@ -409,7 +409,7 @@ const transformTypeReferenceNode = (
|
|
|
409
409
|
|
|
410
410
|
// Array<T> / ReadonlyArray<T> to readonly T[]
|
|
411
411
|
if (typeNameStr === 'Array' || typeNameStr === 'ReadonlyArray') {
|
|
412
|
-
if (!Arr.
|
|
412
|
+
if (!Arr.isFixedLengthArray(1, typeArguments)) {
|
|
413
413
|
throw new Error(
|
|
414
414
|
`Unexpected number of type arguments "${typeArguments.length}" for ${typeNameStr}.`,
|
|
415
415
|
);
|
|
@@ -442,7 +442,7 @@ const transformTypeReferenceNode = (
|
|
|
442
442
|
|
|
443
443
|
// Set<T> to ReadonlySet<T>
|
|
444
444
|
if (typeNameStr === 'Set') {
|
|
445
|
-
if (!Arr.
|
|
445
|
+
if (!Arr.isFixedLengthArray(1, typeArguments)) {
|
|
446
446
|
throw new Error(
|
|
447
447
|
`Unexpected number of type arguments "${typeArguments.length}" for Set.`,
|
|
448
448
|
);
|
|
@@ -468,7 +468,7 @@ const transformTypeReferenceNode = (
|
|
|
468
468
|
|
|
469
469
|
// Map<T> to ReadonlyMap<T>
|
|
470
470
|
if (typeNameStr === 'Map') {
|
|
471
|
-
if (!Arr.
|
|
471
|
+
if (!Arr.isFixedLengthArray(2, typeArguments)) {
|
|
472
472
|
throw new Error(
|
|
473
473
|
`Unexpected number of type arguments "${typeArguments.length}" for Map.`,
|
|
474
474
|
);
|
|
@@ -498,7 +498,7 @@ const transformTypeReferenceNode = (
|
|
|
498
498
|
|
|
499
499
|
// remove unnecessary `Readonly` wrapper or convert to readonly operator
|
|
500
500
|
if (typeNameStr === 'Readonly') {
|
|
501
|
-
if (!Arr.
|
|
501
|
+
if (!Arr.isFixedLengthArray(1, typeArguments)) {
|
|
502
502
|
throw new Error(
|
|
503
503
|
`Unexpected number of type arguments "${typeArguments.length}" for Readonly.`,
|
|
504
504
|
);
|
|
@@ -592,7 +592,7 @@ const transformTypeReferenceNode = (
|
|
|
592
592
|
|
|
593
593
|
// DeepReadonly
|
|
594
594
|
if (typeNameStr === options.DeepReadonly.typeName) {
|
|
595
|
-
if (!Arr.
|
|
595
|
+
if (!Arr.isFixedLengthArray(1, typeArguments)) {
|
|
596
596
|
throw new Error(
|
|
597
597
|
`Unexpected number of type arguments "${typeArguments.length}" for Readonly.`,
|
|
598
598
|
);
|
|
@@ -858,10 +858,7 @@ const transformTypeLiteralNode = (
|
|
|
858
858
|
>,
|
|
859
859
|
options: ReadonlyTransformerOptionsInternal,
|
|
860
860
|
): void => {
|
|
861
|
-
if (
|
|
862
|
-
options.ignoreEmptyObjectTypes &&
|
|
863
|
-
Arr.isArrayOfLength(node.getMembers(), 0)
|
|
864
|
-
) {
|
|
861
|
+
if (options.ignoreEmptyObjectTypes && Arr.isEmpty(node.getMembers())) {
|
|
865
862
|
return;
|
|
866
863
|
}
|
|
867
864
|
|
|
@@ -1170,8 +1167,7 @@ const transformUnionOrIntersectionTypeNodeImpl = (
|
|
|
1170
1167
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
1171
1168
|
n: tsm.TypeLiteralNode | ReadonlyTypeReferenceNode,
|
|
1172
1169
|
): boolean =>
|
|
1173
|
-
n.isKind(tsm.SyntaxKind.TypeLiteral) &&
|
|
1174
|
-
Arr.isArrayOfLength(n.getMembers(), 0);
|
|
1170
|
+
n.isKind(tsm.SyntaxKind.TypeLiteral) && Arr.isEmpty(n.getMembers());
|
|
1175
1171
|
|
|
1176
1172
|
const nonEmptyTypeLiterals =
|
|
1177
1173
|
typeLiterals === undefined
|
|
@@ -1183,11 +1179,10 @@ const transformUnionOrIntersectionTypeNodeImpl = (
|
|
|
1183
1179
|
const emptyTypeLiterals =
|
|
1184
1180
|
typeLiterals === undefined || !options.ignoreEmptyObjectTypes
|
|
1185
1181
|
? undefined
|
|
1186
|
-
: typeLiterals.nodes.filter(
|
|
1182
|
+
: typeLiterals.nodes.filter(isEmptyTypeLiteral);
|
|
1187
1183
|
|
|
1188
1184
|
const typeLiteralsWrappedWithReadonly: readonly [] | readonly [string] =
|
|
1189
|
-
nonEmptyTypeLiterals === undefined ||
|
|
1190
|
-
Arr.isArrayOfLength(nonEmptyTypeLiterals, 0)
|
|
1185
|
+
nonEmptyTypeLiterals === undefined || Arr.isEmpty(nonEmptyTypeLiterals)
|
|
1191
1186
|
? ([] as const)
|
|
1192
1187
|
: ([
|
|
1193
1188
|
unionToString({
|
|
@@ -1282,9 +1277,9 @@ const unionToString = ({
|
|
|
1282
1277
|
op: '&' | '|';
|
|
1283
1278
|
wrapWithReadonly: boolean | string;
|
|
1284
1279
|
}>): string =>
|
|
1285
|
-
Arr.
|
|
1280
|
+
Arr.isEmpty(types)
|
|
1286
1281
|
? 'never'
|
|
1287
|
-
: Arr.
|
|
1282
|
+
: Arr.isFixedLengthArray(1, types)
|
|
1288
1283
|
? wrapWithReadonly === false
|
|
1289
1284
|
? wrapWithParentheses(types[0])
|
|
1290
1285
|
: (`${isString(wrapWithReadonly) ? wrapWithReadonly : 'Readonly'}<${types[0]}>` as const)
|
|
@@ -24,9 +24,7 @@ export const replaceRecordWithUnknownRecordTransformer =
|
|
|
24
24
|
name: TRANSFORMER_NAME,
|
|
25
25
|
transform: (sourceAst) => {
|
|
26
26
|
const containerModifications = Arr.toUnshifted(
|
|
27
|
-
sourceAst
|
|
28
|
-
.getModules()
|
|
29
|
-
.map((namespace) => processDeclarations(namespace)),
|
|
27
|
+
sourceAst.getModules().map(processDeclarations),
|
|
30
28
|
processDeclarations(sourceAst),
|
|
31
29
|
);
|
|
32
30
|
|
|
@@ -53,7 +51,7 @@ const processDeclarations = (
|
|
|
53
51
|
|
|
54
52
|
const interfaceModifications = container
|
|
55
53
|
.getInterfaces()
|
|
56
|
-
.map(
|
|
54
|
+
.map(processInterfaceDeclaration);
|
|
57
55
|
|
|
58
56
|
return [...typeAliasModifications, ...interfaceModifications].includes(true);
|
|
59
57
|
};
|
|
@@ -81,10 +79,10 @@ const processInterfaceDeclaration = (
|
|
|
81
79
|
// (type parameters, extends clauses, declare modifier, default export).
|
|
82
80
|
if (
|
|
83
81
|
hasStringUnknownSignature &&
|
|
84
|
-
Arr.
|
|
85
|
-
Arr.
|
|
86
|
-
Arr.
|
|
87
|
-
Arr.
|
|
82
|
+
Arr.isEmpty(interfaceDecl.getProperties()) &&
|
|
83
|
+
Arr.isFixedLengthArray(1, indexSignatures) &&
|
|
84
|
+
Arr.isEmpty(interfaceDecl.getTypeParameters()) &&
|
|
85
|
+
Arr.isEmpty(interfaceDecl.getExtends()) &&
|
|
88
86
|
!interfaceDecl.hasDeclareKeyword() &&
|
|
89
87
|
!interfaceDecl.hasDefaultKeyword()
|
|
90
88
|
) {
|
|
@@ -118,7 +116,7 @@ const visitTypeNode = (node: tsm.TypeNode): boolean => {
|
|
|
118
116
|
if (node.getTypeName().getText() === 'Readonly') {
|
|
119
117
|
const typeArgs = node.getTypeArguments();
|
|
120
118
|
|
|
121
|
-
if (Arr.
|
|
119
|
+
if (Arr.isFixedLengthArray(1, typeArgs)) {
|
|
122
120
|
const typeArg = typeArgs[0];
|
|
123
121
|
|
|
124
122
|
if (tsm.Node.isTypeLiteral(typeArg)) {
|
|
@@ -130,8 +128,8 @@ const visitTypeNode = (node: tsm.TypeNode): boolean => {
|
|
|
130
128
|
|
|
131
129
|
// Check if it has only one index signature [k: string]: unknown
|
|
132
130
|
if (
|
|
133
|
-
Arr.
|
|
134
|
-
Arr.
|
|
131
|
+
Arr.isFixedLengthArray(1, members) &&
|
|
132
|
+
Arr.isFixedLengthArray(1, indexSigs) &&
|
|
135
133
|
isStringUnknownIndexSignature(indexSigs[0])
|
|
136
134
|
) {
|
|
137
135
|
node.replaceWithText('UnknownRecord');
|
|
@@ -158,8 +156,8 @@ const visitTypeNode = (node: tsm.TypeNode): boolean => {
|
|
|
158
156
|
|
|
159
157
|
// Check if it has only one index signature [k: string]: unknown
|
|
160
158
|
if (
|
|
161
|
-
Arr.
|
|
162
|
-
Arr.
|
|
159
|
+
Arr.isFixedLengthArray(1, members) &&
|
|
160
|
+
Arr.isFixedLengthArray(1, indexSigs) &&
|
|
163
161
|
isStringUnknownIndexSignature(indexSigs[0])
|
|
164
162
|
) {
|
|
165
163
|
node.replaceWithText('UnknownRecord');
|
|
@@ -188,10 +186,7 @@ const visitTypeNode = (node: tsm.TypeNode): boolean => {
|
|
|
188
186
|
|
|
189
187
|
// Recursively visit child type nodes
|
|
190
188
|
if (tsm.Node.isUnionTypeNode(node) || tsm.Node.isIntersectionTypeNode(node)) {
|
|
191
|
-
return node
|
|
192
|
-
.getTypeNodes()
|
|
193
|
-
.map((typeNode) => visitTypeNode(typeNode))
|
|
194
|
-
.includes(true);
|
|
189
|
+
return node.getTypeNodes().map(visitTypeNode).includes(true);
|
|
195
190
|
}
|
|
196
191
|
|
|
197
192
|
if (tsm.Node.isArrayTypeNode(node)) {
|
|
@@ -199,10 +194,7 @@ const visitTypeNode = (node: tsm.TypeNode): boolean => {
|
|
|
199
194
|
}
|
|
200
195
|
|
|
201
196
|
if (tsm.Node.isTupleTypeNode(node)) {
|
|
202
|
-
return node
|
|
203
|
-
.getElements()
|
|
204
|
-
.map((element) => visitTypeNode(element))
|
|
205
|
-
.includes(true);
|
|
197
|
+
return node.getElements().map(visitTypeNode).includes(true);
|
|
206
198
|
}
|
|
207
199
|
|
|
208
200
|
if (tsm.Node.isParenthesizedTypeNode(node)) {
|
|
@@ -244,7 +236,7 @@ const replaceIfRecordUnknown = (node: tsm.TypeReferenceNode): boolean => {
|
|
|
244
236
|
const typeArgs = node.getTypeArguments();
|
|
245
237
|
|
|
246
238
|
if (
|
|
247
|
-
Arr.
|
|
239
|
+
Arr.isFixedLengthArray(2, typeArgs) &&
|
|
248
240
|
typeArgs[0].getText() === 'string' &&
|
|
249
241
|
typeArgs[1].getText() === 'unknown'
|
|
250
242
|
) {
|
|
@@ -254,13 +246,13 @@ const replaceIfRecordUnknown = (node: tsm.TypeReferenceNode): boolean => {
|
|
|
254
246
|
}
|
|
255
247
|
|
|
256
248
|
// Recurse into type arguments (e.g. Record<string, Record<string, unknown>>)
|
|
257
|
-
return typeArgs.map(
|
|
249
|
+
return typeArgs.map(visitTypeNode).includes(true);
|
|
258
250
|
}
|
|
259
251
|
case 'Readonly': {
|
|
260
252
|
// Check if it's Readonly<Record<string, unknown>>
|
|
261
253
|
const typeArgs = node.getTypeArguments();
|
|
262
254
|
|
|
263
|
-
if (Arr.
|
|
255
|
+
if (Arr.isFixedLengthArray(1, typeArgs)) {
|
|
264
256
|
const innerType = typeArgs[0];
|
|
265
257
|
|
|
266
258
|
if (tsm.Node.isTypeReference(innerType)) {
|
|
@@ -270,7 +262,7 @@ const replaceIfRecordUnknown = (node: tsm.TypeReferenceNode): boolean => {
|
|
|
270
262
|
const innerTypeArgs = innerType.getTypeArguments();
|
|
271
263
|
|
|
272
264
|
if (
|
|
273
|
-
Arr.
|
|
265
|
+
Arr.isFixedLengthArray(2, innerTypeArgs) &&
|
|
274
266
|
innerTypeArgs[0].getText() === 'string' &&
|
|
275
267
|
innerTypeArgs[1].getText() === 'unknown'
|
|
276
268
|
) {
|
|
@@ -290,10 +282,7 @@ const replaceIfRecordUnknown = (node: tsm.TypeReferenceNode): boolean => {
|
|
|
290
282
|
|
|
291
283
|
default: {
|
|
292
284
|
// Recurse into type arguments for any other generic type
|
|
293
|
-
return node
|
|
294
|
-
.getTypeArguments()
|
|
295
|
-
.map((typeArg) => visitTypeNode(typeArg))
|
|
296
|
-
.includes(true);
|
|
285
|
+
return node.getTypeArguments().map(visitTypeNode).includes(true);
|
|
297
286
|
}
|
|
298
287
|
}
|
|
299
288
|
};
|
|
@@ -58,12 +58,12 @@ const shouldSkipFile = (
|
|
|
58
58
|
|
|
59
59
|
const hasFileIgnoreComment = patterns.some((regex) => regex.test(code));
|
|
60
60
|
|
|
61
|
-
if (Arr.
|
|
61
|
+
if (Arr.isEmpty(ignoredTransformers) && !hasFileIgnoreComment) {
|
|
62
62
|
return false;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
// Empty array means ignore all transformers (file-level ignore without specific transformers)
|
|
66
|
-
if (Arr.
|
|
66
|
+
if (Arr.isEmpty(ignoredTransformers)) {
|
|
67
67
|
return true;
|
|
68
68
|
}
|
|
69
69
|
|
|
@@ -43,6 +43,6 @@ export const isAsConstNode = (
|
|
|
43
43
|
// and that there are no type arguments (as const doesn't have them)
|
|
44
44
|
return (
|
|
45
45
|
typeNameNode.getText() === 'const' &&
|
|
46
|
-
Arr.
|
|
46
|
+
Arr.isEmpty(typeNode.getTypeArguments())
|
|
47
47
|
);
|
|
48
48
|
};
|
|
@@ -176,7 +176,7 @@ export const isReadonlyTypeReferenceNode = (
|
|
|
176
176
|
return (
|
|
177
177
|
typeName.isKind(tsm.SyntaxKind.Identifier) &&
|
|
178
178
|
typeName.getText() === 'Readonly' &&
|
|
179
|
-
Arr.
|
|
179
|
+
Arr.isFixedLengthArray(1, typeArguments)
|
|
180
180
|
);
|
|
181
181
|
};
|
|
182
182
|
|