ts-codemod-lib 2.2.4 → 3.0.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 (51) hide show
  1. package/README.md +15 -54
  2. package/dist/functions/ast-transformers/convert-to-readonly.d.mts.map +1 -1
  3. package/dist/functions/ast-transformers/convert-to-readonly.mjs +2 -3
  4. package/dist/functions/ast-transformers/convert-to-readonly.mjs.map +1 -1
  5. package/dist/functions/ast-transformers/readonly-transformer-helpers/readonly-context.d.mts +3 -3
  6. package/dist/functions/ast-transformers/readonly-transformer-helpers/readonly-context.d.mts.map +1 -1
  7. package/dist/functions/ast-transformers/readonly-transformer-helpers/readonly-context.mjs.map +1 -1
  8. package/dist/functions/ast-transformers/transform-source-code.mjs +1 -1
  9. package/dist/functions/ast-transformers/transform-source-code.mjs.map +1 -1
  10. package/dist/functions/functions/is-readonly-node.d.mts +5 -5
  11. package/dist/functions/functions/is-readonly-node.d.mts.map +1 -1
  12. package/dist/functions/functions/is-readonly-node.mjs.map +1 -1
  13. package/dist/functions/functions/should-avoid-parentheses-for-readonly.d.mts.map +1 -1
  14. package/dist/functions/functions/should-avoid-parentheses-for-readonly.mjs +1 -4
  15. package/dist/functions/functions/should-avoid-parentheses-for-readonly.mjs.map +1 -1
  16. package/package.json +69 -115
  17. package/src/functions/ast-transformers/convert-to-readonly.mts +20 -17
  18. package/src/functions/ast-transformers/readonly-transformer-helpers/readonly-context.mts +3 -3
  19. package/src/functions/ast-transformers/transform-source-code.mts +1 -1
  20. package/src/functions/functions/is-readonly-node.mts +5 -5
  21. package/src/functions/functions/should-avoid-parentheses-for-readonly.mts +1 -5
  22. package/dist/cmd/append-as-const.d.mts +0 -3
  23. package/dist/cmd/append-as-const.d.mts.map +0 -1
  24. package/dist/cmd/append-as-const.mjs +0 -63
  25. package/dist/cmd/append-as-const.mjs.map +0 -1
  26. package/dist/cmd/convert-interface-to-type.d.mts +0 -3
  27. package/dist/cmd/convert-interface-to-type.d.mts.map +0 -1
  28. package/dist/cmd/convert-interface-to-type.mjs +0 -63
  29. package/dist/cmd/convert-interface-to-type.mjs.map +0 -1
  30. package/dist/cmd/convert-to-readonly.d.mts +0 -3
  31. package/dist/cmd/convert-to-readonly.d.mts.map +0 -1
  32. package/dist/cmd/convert-to-readonly.mjs +0 -63
  33. package/dist/cmd/convert-to-readonly.mjs.map +0 -1
  34. package/dist/cmd/replace-any-with-unknown.d.mts +0 -3
  35. package/dist/cmd/replace-any-with-unknown.d.mts.map +0 -1
  36. package/dist/cmd/replace-any-with-unknown.mjs +0 -63
  37. package/dist/cmd/replace-any-with-unknown.mjs.map +0 -1
  38. package/dist/cmd/replace-record-with-unknown-record.d.mts +0 -3
  39. package/dist/cmd/replace-record-with-unknown-record.d.mts.map +0 -1
  40. package/dist/cmd/replace-record-with-unknown-record.mjs +0 -63
  41. package/dist/cmd/replace-record-with-unknown-record.mjs.map +0 -1
  42. package/dist/cmd/run-transformer-cli.d.mts +0 -13
  43. package/dist/cmd/run-transformer-cli.d.mts.map +0 -1
  44. package/dist/cmd/run-transformer-cli.mjs +0 -176
  45. package/dist/cmd/run-transformer-cli.mjs.map +0 -1
  46. package/src/cmd/append-as-const.mts +0 -67
  47. package/src/cmd/convert-interface-to-type.mts +0 -67
  48. package/src/cmd/convert-to-readonly.mts +0 -67
  49. package/src/cmd/replace-any-with-unknown.mts +0 -67
  50. package/src/cmd/replace-record-with-unknown-record.mts +0 -67
  51. package/src/cmd/run-transformer-cli.mts +0 -307
@@ -1,19 +1,19 @@
1
1
  import * as tsm from 'ts-morph';
2
- import { type DeepReadonly } from 'ts-type-forge';
2
+ import { type DeepReadonly, type StrictOmit } from 'ts-type-forge';
3
3
  export declare const isShallowReadonlyTypeNode: (node: tsm.Node) => boolean;
4
4
  export declare const isReadonlyTupleOrArrayTypeNode: (node: DeepReadonly<tsm.Node>) => node is ReadonlyArrayTypeNode | ReadonlyTupleTypeNode;
5
- export type ReadonlyArrayTypeNode = tsm.TypeNode & Omit<tsm.TypeOperatorTypeNode, 'getOperator' | 'getTypeNode'> & Readonly<{
5
+ export type ReadonlyArrayTypeNode = tsm.TypeNode & StrictOmit<tsm.TypeOperatorTypeNode, 'getOperator' | 'getTypeNode'> & Readonly<{
6
6
  getOperator: () => tsm.SyntaxKind.ReadonlyKeyword;
7
7
  getTypeNode: () => tsm.ArrayTypeNode;
8
8
  }>;
9
9
  export declare const isReadonlyArrayTypeNode: (node: DeepReadonly<tsm.Node>) => node is ReadonlyArrayTypeNode;
10
- export type ReadonlyTupleTypeNode = tsm.TypeNode & Omit<tsm.TypeOperatorTypeNode, 'getOperator' | 'getTypeNode'> & Readonly<{
10
+ export type ReadonlyTupleTypeNode = tsm.TypeNode & StrictOmit<tsm.TypeOperatorTypeNode, 'getOperator' | 'getTypeNode'> & Readonly<{
11
11
  getOperator: () => tsm.SyntaxKind.ReadonlyKeyword;
12
12
  getTypeNode: () => tsm.TupleTypeNode;
13
13
  }>;
14
14
  export declare const isReadonlyTupleTypeNode: (node: DeepReadonly<tsm.Node>) => node is ReadonlyTupleTypeNode;
15
- export type ReadonlyTypeReferenceNode = tsm.TypeNode & Omit<tsm.TypeReferenceNode, 'getTypeName' | 'getTypeArguments'> & Readonly<{
16
- getTypeName: () => Omit<tsm.Identifier, 'getText'> & Readonly<{
15
+ export type ReadonlyTypeReferenceNode = tsm.TypeNode & StrictOmit<tsm.TypeReferenceNode, 'getTypeName' | 'getTypeArguments'> & Readonly<{
16
+ getTypeName: () => StrictOmit<tsm.Identifier, 'getText'> & Readonly<{
17
17
  getText: () => 'Readonly';
18
18
  }>;
19
19
  getTypeArguments: () => readonly [tsm.TypeNode];
@@ -1 +1 @@
1
- {"version":3,"file":"is-readonly-node.d.mts","sourceRoot":"","sources":["../../../src/functions/functions/is-readonly-node.mts"],"names":[],"mappings":"AACA,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAChC,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAGlD,eAAO,MAAM,yBAAyB,SAAU,GAAG,CAAC,IAAI,KAAG,OAGnC,CAAC;AAEzB,eAAO,MAAM,8BAA8B,SACnC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,KAC3B,IAAI,IAAI,qBAAqB,GAAG,qBAIqB,CAAC;AAEzD,MAAM,MAAM,qBAAqB,GAAG,GAAG,CAAC,QAAQ,GAC9C,IAAI,CAAC,GAAG,CAAC,oBAAoB,EAAE,aAAa,GAAG,aAAa,CAAC,GAC7D,QAAQ,CAAC;IACP,WAAW,EAAE,MAAM,GAAG,CAAC,UAAU,CAAC,eAAe,CAAC;IAClD,WAAW,EAAE,MAAM,GAAG,CAAC,aAAa,CAAC;CACtC,CAAC,CAAC;AAEL,eAAO,MAAM,uBAAuB,SAC5B,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,KAC3B,IAAI,IAAI,qBAG0C,CAAC;AA8DtD,MAAM,MAAM,qBAAqB,GAAG,GAAG,CAAC,QAAQ,GAC9C,IAAI,CAAC,GAAG,CAAC,oBAAoB,EAAE,aAAa,GAAG,aAAa,CAAC,GAC7D,QAAQ,CAAC;IACP,WAAW,EAAE,MAAM,GAAG,CAAC,UAAU,CAAC,eAAe,CAAC;IAClD,WAAW,EAAE,MAAM,GAAG,CAAC,aAAa,CAAC;CACtC,CAAC,CAAC;AAEL,eAAO,MAAM,uBAAuB,SAC5B,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,KAC3B,IAAI,IAAI,qBAG0C,CAAC;AAkDtD,MAAM,MAAM,yBAAyB,GAAG,GAAG,CAAC,QAAQ,GAClD,IAAI,CAAC,GAAG,CAAC,iBAAiB,EAAE,aAAa,GAAG,kBAAkB,CAAC,GAC/D,QAAQ,CAAC;IACP,WAAW,EAAE,MAAM,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,SAAS,CAAC,GAChD,QAAQ,CAAC;QACP,OAAO,EAAE,MAAM,UAAU,CAAC;KAC3B,CAAC,CAAC;IACL,gBAAgB,EAAE,MAAM,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;CACjD,CAAC,CAAC;AAEL,eAAO,MAAM,2BAA2B,SAChC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,KAC3B,IAAI,IAAI,yBAcV,CAAC"}
1
+ {"version":3,"file":"is-readonly-node.d.mts","sourceRoot":"","sources":["../../../src/functions/functions/is-readonly-node.mts"],"names":[],"mappings":"AACA,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAChC,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,UAAU,EAAE,MAAM,eAAe,CAAC;AAGnE,eAAO,MAAM,yBAAyB,SAAU,GAAG,CAAC,IAAI,KAAG,OAGnC,CAAC;AAEzB,eAAO,MAAM,8BAA8B,SACnC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,KAC3B,IAAI,IAAI,qBAAqB,GAAG,qBAIqB,CAAC;AAEzD,MAAM,MAAM,qBAAqB,GAAG,GAAG,CAAC,QAAQ,GAC9C,UAAU,CAAC,GAAG,CAAC,oBAAoB,EAAE,aAAa,GAAG,aAAa,CAAC,GACnE,QAAQ,CAAC;IACP,WAAW,EAAE,MAAM,GAAG,CAAC,UAAU,CAAC,eAAe,CAAC;IAClD,WAAW,EAAE,MAAM,GAAG,CAAC,aAAa,CAAC;CACtC,CAAC,CAAC;AAEL,eAAO,MAAM,uBAAuB,SAC5B,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,KAC3B,IAAI,IAAI,qBAG0C,CAAC;AA8DtD,MAAM,MAAM,qBAAqB,GAAG,GAAG,CAAC,QAAQ,GAC9C,UAAU,CAAC,GAAG,CAAC,oBAAoB,EAAE,aAAa,GAAG,aAAa,CAAC,GACnE,QAAQ,CAAC;IACP,WAAW,EAAE,MAAM,GAAG,CAAC,UAAU,CAAC,eAAe,CAAC;IAClD,WAAW,EAAE,MAAM,GAAG,CAAC,aAAa,CAAC;CACtC,CAAC,CAAC;AAEL,eAAO,MAAM,uBAAuB,SAC5B,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,KAC3B,IAAI,IAAI,qBAG0C,CAAC;AAkDtD,MAAM,MAAM,yBAAyB,GAAG,GAAG,CAAC,QAAQ,GAClD,UAAU,CAAC,GAAG,CAAC,iBAAiB,EAAE,aAAa,GAAG,kBAAkB,CAAC,GACrE,QAAQ,CAAC;IACP,WAAW,EAAE,MAAM,UAAU,CAAC,GAAG,CAAC,UAAU,EAAE,SAAS,CAAC,GACtD,QAAQ,CAAC;QACP,OAAO,EAAE,MAAM,UAAU,CAAC;KAC3B,CAAC,CAAC;IACL,gBAAgB,EAAE,MAAM,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;CACjD,CAAC,CAAC;AAEL,eAAO,MAAM,2BAA2B,SAChC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,KAC3B,IAAI,IAAI,yBAcV,CAAC"}
@@ -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.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;;;;"}
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, type StrictOmit } 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 StrictOmit<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 StrictOmit<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 StrictOmit<tsm.TypeReferenceNode, 'getTypeName' | 'getTypeArguments'> &\n Readonly<{\n getTypeName: () => StrictOmit<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;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"should-avoid-parentheses-for-readonly.d.mts","sourceRoot":"","sources":["../../../src/functions/functions/should-avoid-parentheses-for-readonly.mts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAEhC;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,iCAAiC,SAAU,GAAG,CAAC,IAAI,KAAG,OAgBlE,CAAC"}
1
+ {"version":3,"file":"should-avoid-parentheses-for-readonly.d.mts","sourceRoot":"","sources":["../../../src/functions/functions/should-avoid-parentheses-for-readonly.mts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAEhC;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,iCAAiC,SAAU,GAAG,CAAC,IAAI,KAAG,OAYlE,CAAC"}
@@ -5,10 +5,7 @@ const shouldAvoidParenthesesForReadonly = (node) => {
5
5
  if (parent === void 0) {
6
6
  return false;
7
7
  }
8
- if (parent.isKind(tsm.SyntaxKind.TypePredicate)) {
9
- return true;
10
- }
11
- return false;
8
+ return parent.isKind(tsm.SyntaxKind.TypePredicate);
12
9
  };
13
10
 
14
11
  export { shouldAvoidParenthesesForReadonly };
@@ -1 +1 @@
1
- {"version":3,"file":"should-avoid-parentheses-for-readonly.mjs","sources":["../../../src/functions/functions/should-avoid-parentheses-for-readonly.mts"],"sourcesContent":["import * as tsm from 'ts-morph';\n\n/**\n * Determines if parentheses should be avoided when adding 'readonly' prefix to a type.\n *\n * Parentheses should be AVOIDED in these specific contexts:\n * - Type predicates: `x is [T, U]` -> `x is readonly [T, U]` (not `x is (readonly [T, U])`)\n * because parentheses around the type in a type predicate cause syntax errors\n *\n * For all other contexts, we keep parentheses for safety to avoid precedence issues,\n * relying on prettier to remove unnecessary ones.\n *\n * @param node - The type node to check\n * @returns true if parentheses should be avoided, false otherwise (keep parentheses)\n */\nexport const shouldAvoidParenthesesForReadonly = (node: tsm.Node): boolean => {\n const parent = node.getParent();\n\n if (parent === undefined) {\n return false;\n }\n\n // Check if parent is TypePredicate: x is T\n // In this case, we MUST avoid parentheses: x is readonly T (not x is (readonly T))\n // because `x is (readonly T)` is a syntax error\n if (parent.isKind(tsm.SyntaxKind.TypePredicate)) {\n return true;\n }\n\n // For all other cases, keep parentheses for safety\n return false;\n};\n"],"names":[],"mappings":";;AAeO,MAAM,iCAAA,GAAoC,CAAC,IAAA,KAA4B;AAC5E,EAAA,MAAM,MAAA,GAAS,KAAK,SAAA,EAAU;AAE9B,EAAA,IAAI,WAAW,MAAA,EAAW;AACxB,IAAA,OAAO,KAAA;AAAA,EACT;AAKA,EAAA,IAAI,MAAA,CAAO,MAAA,CAAO,GAAA,CAAI,UAAA,CAAW,aAAa,CAAA,EAAG;AAC/C,IAAA,OAAO,IAAA;AAAA,EACT;AAGA,EAAA,OAAO,KAAA;AACT;;;;"}
1
+ {"version":3,"file":"should-avoid-parentheses-for-readonly.mjs","sources":["../../../src/functions/functions/should-avoid-parentheses-for-readonly.mts"],"sourcesContent":["import * as tsm from 'ts-morph';\n\n/**\n * Determines if parentheses should be avoided when adding 'readonly' prefix to a type.\n *\n * Parentheses should be AVOIDED in these specific contexts:\n * - Type predicates: `x is [T, U]` -> `x is readonly [T, U]` (not `x is (readonly [T, U])`)\n * because parentheses around the type in a type predicate cause syntax errors\n *\n * For all other contexts, we keep parentheses for safety to avoid precedence issues,\n * relying on prettier to remove unnecessary ones.\n *\n * @param node - The type node to check\n * @returns true if parentheses should be avoided, false otherwise (keep parentheses)\n */\nexport const shouldAvoidParenthesesForReadonly = (node: tsm.Node): boolean => {\n const parent = node.getParent();\n\n if (parent === undefined) {\n return false;\n }\n\n // Check if parent is TypePredicate: x is T\n // In this case, we MUST avoid parentheses: x is readonly T (not x is (readonly T))\n // because `x is (readonly T)` is a syntax error\n // For all other cases, keep parentheses for safety\n return parent.isKind(tsm.SyntaxKind.TypePredicate);\n};\n"],"names":[],"mappings":";;AAeO,MAAM,iCAAA,GAAoC,CAAC,IAAA,KAA4B;AAC5E,EAAA,MAAM,MAAA,GAAS,KAAK,SAAA,EAAU;AAE9B,EAAA,IAAI,WAAW,MAAA,EAAW;AACxB,IAAA,OAAO,KAAA;AAAA,EACT;AAMA,EAAA,OAAO,MAAA,CAAO,MAAA,CAAO,GAAA,CAAI,UAAA,CAAW,aAAa,CAAA;AACnD;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ts-codemod-lib",
3
- "version": "2.2.4",
3
+ "version": "3.0.0",
4
4
  "private": false,
5
5
  "keywords": [
6
6
  "typescript",
@@ -11,13 +11,11 @@
11
11
  ],
12
12
  "repository": {
13
13
  "type": "git",
14
- "url": "git+https://github.com/noshiro-pf/ts-codemod-lib.git"
14
+ "url": "git+https://github.com/noshiro-pf/mono.git",
15
+ "directory": "libs/ts-codemod-lib"
15
16
  },
16
17
  "license": "Apache-2.0",
17
18
  "author": "noshiro-pf <noshiro.pf@gmail.com>",
18
- "sideEffects": [
19
- "./dist/cmd/*.mjs"
20
- ],
21
19
  "type": "module",
22
20
  "exports": {
23
21
  ".": {
@@ -33,144 +31,49 @@
33
31
  },
34
32
  "module": "./dist/entry-point.mjs",
35
33
  "types": "./dist/types.d.mts",
36
- "bin": {
37
- "append-as-const": "./dist/cmd/append-as-const.mjs",
38
- "convert-interface-to-type": "./dist/cmd/convert-interface-to-type.mjs",
39
- "convert-to-readonly": "./dist/cmd/convert-to-readonly.mjs",
40
- "replace-any-with-unknown": "./dist/cmd/replace-any-with-unknown.mjs",
41
- "replace-record-with-unknown-record": "./dist/cmd/replace-record-with-unknown-record.mjs"
42
- },
43
34
  "files": [
44
35
  "src",
45
36
  "dist",
46
37
  "README.md",
47
38
  "LICENSE"
48
39
  ],
49
- "scripts": {
50
- "agents:gen": "tsx ./scripts/cmd/gen-agents-md.mts",
51
- "agents:sync": "tsx ./scripts/cmd/sync-agent-config.mts",
52
- "build": "tsx ./scripts/cmd/build.mts",
53
- "build:min": "tsx ./scripts/cmd/build.mts --skip-check",
54
- "check-all": "tsx ./scripts/cmd/check-all.mts",
55
- "check:ext": "tsx ./scripts/cmd/check-ext.mts",
56
- "clean": "pnpm run /clean:.*/",
57
- "clean:logs": "npx rimraf ./*.log",
58
- "codemod": "run-s codemod:uncommitted:* fmt",
59
- "codemod:diff": "run-s codemod:diff:* fmt",
60
- "codemod:diff:any-to-unknown": "tsx ./src/cmd/replace-any-with-unknown.mts --diff-from origin/main '{src,scripts,samples,test}/**/*.{mts,tsx}'",
61
- "codemod:diff:as-const": "tsx ./src/cmd/append-as-const.mts --diff-from origin/main '{src,scripts,samples,test}/**/*.{mts,tsx}'",
62
- "codemod:diff:interface-to-type": "tsx ./src/cmd/convert-interface-to-type.mts --diff-from origin/main '{src,scripts,samples,test}/**/*.{mts,tsx}'",
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}'",
65
- "codemod:full": "run-s codemod:full:* fmt",
66
- "codemod:full:any-to-unknown": "tsx ./src/cmd/replace-any-with-unknown.mts '{src,scripts,samples,test}/**/*.{mts,tsx}'",
67
- "codemod:full:as-const": "tsx ./src/cmd/append-as-const.mts '{src,scripts,samples,test}/**/*.{mts,tsx}'",
68
- "codemod:full:interface-to-type": "tsx ./src/cmd/convert-interface-to-type.mts '{src,scripts,samples,test}/**/*.{mts,tsx}'",
69
- "codemod:full:readonly": "tsx ./src/cmd/convert-to-readonly.mts '{src,scripts,samples,test}/**/*.{mts,tsx}'",
70
- "codemod:full:unknown-record": "tsx ./src/cmd/replace-record-with-unknown-record.mts '{src,scripts,samples,test}/**/*.{mts,tsx}'",
71
- "codemod:uncommitted:any-to-unknown": "tsx ./src/cmd/replace-any-with-unknown.mts --uncommitted '{src,scripts,samples,test}/**/*.{mts,tsx}'",
72
- "codemod:uncommitted:as-const": "tsx ./src/cmd/append-as-const.mts --uncommitted '{src,scripts,samples,test}/**/*.{mts,tsx}'",
73
- "codemod:uncommitted:interface-to-type": "tsx ./src/cmd/convert-interface-to-type.mts --uncommitted '{src,scripts,samples,test}/**/*.{mts,tsx}'",
74
- "codemod:uncommitted:readonly": "tsx ./src/cmd/convert-to-readonly.mts --uncommitted '{src,scripts,samples,test}/**/*.{mts,tsx}'",
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",
77
- "doc": "tsx ./scripts/cmd/gen-docs.mts",
78
- "doc:embed": "tsx ./scripts/cmd/embed-examples.mts",
79
- "doc:preview": "vite preview --config ./configs/vite.doc.config.mts",
80
- "doc:watch": "typedoc --options ./configs/typedoc.config.mjs --watch",
81
- "fmt": "format-uncommitted",
82
- "fmt:diff": "format-diff-from origin/main",
83
- "fmt:full": "prettier --write .",
84
- "gi": "run-s gi:src fmt",
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",
86
- "lint": "eslint .",
87
- "lint:fix": "eslint . --fix",
88
- "md": "markdownlint-cli2",
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",
92
- "run:cmd": "tsx ./src/cmd/convert-to-readonly.mts ./test/sample1.mts",
93
- "run:test": "tsx ./scripts/test-code/transform-test-code.mts",
94
- "sync-cli-versions": "tsx scripts/cmd/sync-cli-versions.mts",
95
- "test": "pnpm run z:vitest:node run",
96
- "test:browser": "pnpm run z:vitest --project='Browser' run",
97
- "test:cov": "pnpm run z:vitest:node run --coverage",
98
- "test:cov:ui": "vite preview --outDir ./coverage",
99
- "test:ui": "pnpm run z:vitest:node --ui",
100
- "testw": "pnpm run z:vitest:node watch",
101
- "tsc": "node ./node_modules/typescript-native/bin/tsc --noEmit",
102
- "tscw": "node ./node_modules/typescript-native/bin/tsc --noEmit --watch -p ./tsconfig.json",
103
- "type-check": "node ./node_modules/typescript-native/bin/tsc --noEmit",
104
- "z:vitest": "vitest --config ./configs/vitest.config.mts",
105
- "z:vitest:node": "pnpm run z:vitest --project='Node.js'"
106
- },
107
40
  "dependencies": {
108
- "ts-data-forge": "^14.0.0",
109
41
  "ts-morph": "^28.0.0",
110
- "ts-type-forge": "^9.1.1"
42
+ "ts-data-forge": "^14.2.1",
43
+ "ts-type-forge": "^9.2.1"
111
44
  },
112
45
  "devDependencies": {
113
46
  "@rollup/plugin-replace": "6.0.3",
114
47
  "@rollup/plugin-strip": "3.0.4",
115
- "@semantic-release/changelog": "7.0.0",
116
- "@semantic-release/commit-analyzer": "13.0.1",
117
- "@semantic-release/exec": "7.1.0",
118
- "@semantic-release/git": "11.0.1",
119
- "@semantic-release/github": "12.0.9",
120
- "@semantic-release/npm": "13.1.5",
121
- "@semantic-release/release-notes-generator": "14.1.1",
122
- "@types/node": "26.1.2",
48
+ "@types/node": "26.2.0",
123
49
  "@vitest/browser-playwright": "4.1.10",
124
50
  "@vitest/coverage-v8": "4.1.10",
125
51
  "@vitest/ui": "4.1.10",
126
- "cmd-ts": "0.15.0",
127
- "conventional-changelog-conventionalcommits": "10.2.1",
128
52
  "cspell": "10.0.1",
129
53
  "dedent": "1.7.2",
130
54
  "esbuild": "0.28.1",
131
55
  "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",
137
56
  "jiti": "2.7.0",
138
- "markdownlint": "0.41.1",
139
- "markdownlint-cli2": "0.23.2",
140
- "npm-run-all2": "9.0.2",
141
- "playwright": "1.62.0",
57
+ "npm-run-all2": "9.0.3",
58
+ "playwright": "1.62.1",
142
59
  "prettier": "3.9.6",
143
60
  "prettier-plugin-organize-imports": "4.3.0",
144
61
  "prettier-plugin-packagejson": "3.0.2",
145
- "rollup": "4.62.3",
62
+ "rollup": "4.62.4",
146
63
  "rollup-plugin-esbuild": "6.2.1",
147
- "semantic-release": "25.0.8",
148
- "ts-repo-utils": "10.1.6",
149
- "tsx": "4.23.1",
64
+ "tsx": "4.23.4",
150
65
  "typedoc": "0.28.20",
151
66
  "typedoc-github-theme": "0.4.0",
152
67
  "typescript": "6.0.3",
153
68
  "typescript-native": "npm:typescript@7.0.2",
154
- "vite": "8.1.5",
155
- "vitest": "4.1.10"
156
- },
157
- "peerDependencies": {
158
- "cmd-ts": "0.14.3",
159
- "dedent": "1.7.2",
160
- "ts-repo-utils": "8.1.0"
161
- },
162
- "peerDependenciesMeta": {
163
- "cmd-ts": {
164
- "optional": true
165
- },
166
- "dedent": {
167
- "optional": true
168
- },
169
- "ts-repo-utils": {
170
- "optional": true
171
- }
69
+ "vite": "8.2.0",
70
+ "vitest": "4.1.10",
71
+ "eslint-config-typed": "5.8.5",
72
+ "eslint-plugin-ts-data-forge": "0.4.4",
73
+ "eslint-plugin-ts-fortress": "1.0.1",
74
+ "eslint-plugin-ts-type-forge": "0.6.3",
75
+ "ts-repo-utils": "10.2.0"
172
76
  },
173
- "packageManager": "pnpm@11.20.0",
174
77
  "engines": {
175
78
  "node": ">=22.22.2",
176
79
  "pnpm": ">=9.0.0"
@@ -181,5 +84,56 @@
181
84
  "publishConfig": {
182
85
  "access": "public",
183
86
  "provenance": true
87
+ },
88
+ "scripts": {
89
+ "build": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ./scripts/cmd/build.mts",
90
+ "build:min": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ./scripts/cmd/build.mts --skip-check",
91
+ "check-all": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ./scripts/cmd/check-all.mts",
92
+ "check:ext": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ./scripts/cmd/check-ext.mts",
93
+ "clean": "pnpm run /clean:.*/",
94
+ "clean:logs": "npx rimraf ./*.log",
95
+ "codemod": "run-s codemod:uncommitted:* fmt",
96
+ "codemod:diff": "run-s codemod:diff:* fmt",
97
+ "codemod:diff:any-to-unknown": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ../ts-codemod-cli/src/cmd/replace-any-with-unknown.mts --diff-from origin/main '{src,scripts,samples,test}/**/*.{mts,tsx}'",
98
+ "codemod:diff:as-const": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ../ts-codemod-cli/src/cmd/append-as-const.mts --diff-from origin/main '{src,scripts,samples,test}/**/*.{mts,tsx}'",
99
+ "codemod:diff:interface-to-type": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ../ts-codemod-cli/src/cmd/convert-interface-to-type.mts --diff-from origin/main '{src,scripts,samples,test}/**/*.{mts,tsx}'",
100
+ "codemod:diff:readonly": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ../ts-codemod-cli/src/cmd/convert-to-readonly.mts --diff-from origin/main '{src,scripts,samples,test}/**/*.{mts,tsx}'",
101
+ "codemod:diff:unknown-record": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ../ts-codemod-cli/src/cmd/replace-record-with-unknown-record.mts --diff-from origin/main '{src,scripts,samples,test}/**/*.{mts,tsx}'",
102
+ "codemod:full": "run-s codemod:full:* fmt",
103
+ "codemod:full:any-to-unknown": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ../ts-codemod-cli/src/cmd/replace-any-with-unknown.mts '{src,scripts,samples,test}/**/*.{mts,tsx}'",
104
+ "codemod:full:as-const": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ../ts-codemod-cli/src/cmd/append-as-const.mts '{src,scripts,samples,test}/**/*.{mts,tsx}'",
105
+ "codemod:full:interface-to-type": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ../ts-codemod-cli/src/cmd/convert-interface-to-type.mts '{src,scripts,samples,test}/**/*.{mts,tsx}'",
106
+ "codemod:full:readonly": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ../ts-codemod-cli/src/cmd/convert-to-readonly.mts '{src,scripts,samples,test}/**/*.{mts,tsx}'",
107
+ "codemod:full:unknown-record": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ../ts-codemod-cli/src/cmd/replace-record-with-unknown-record.mts '{src,scripts,samples,test}/**/*.{mts,tsx}'",
108
+ "codemod:uncommitted:any-to-unknown": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ../ts-codemod-cli/src/cmd/replace-any-with-unknown.mts --uncommitted '{src,scripts,samples,test}/**/*.{mts,tsx}'",
109
+ "codemod:uncommitted:as-const": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ../ts-codemod-cli/src/cmd/append-as-const.mts --uncommitted '{src,scripts,samples,test}/**/*.{mts,tsx}'",
110
+ "codemod:uncommitted:interface-to-type": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ../ts-codemod-cli/src/cmd/convert-interface-to-type.mts --uncommitted '{src,scripts,samples,test}/**/*.{mts,tsx}'",
111
+ "codemod:uncommitted:readonly": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ../ts-codemod-cli/src/cmd/convert-to-readonly.mts --uncommitted '{src,scripts,samples,test}/**/*.{mts,tsx}'",
112
+ "codemod:uncommitted:unknown-record": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ../ts-codemod-cli/src/cmd/replace-record-with-unknown-record.mts --uncommitted '{src,scripts,samples,test}/**/*.{mts,tsx}'",
113
+ "cspell": "cspell \"**\" --gitignore --gitignore-root ../../ --no-progress",
114
+ "doc": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ./scripts/cmd/gen-docs.mts",
115
+ "doc:embed": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ./scripts/cmd/embed-examples.mts",
116
+ "doc:preview": "vite preview --config ./configs/vite.doc.config.mts",
117
+ "doc:watch": "typedoc --options ./configs/typedoc.config.mjs --watch",
118
+ "fmt": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ../../libs/ts-repo-utils/src/cmd/format-uncommitted.mts --cwd .",
119
+ "fmt:diff": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ../../libs/ts-repo-utils/src/cmd/format-diff-from.mts origin/main --cwd .",
120
+ "fmt:full": "prettier --write .",
121
+ "gi": "run-s gi:src fmt",
122
+ "gi:src": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ../../libs/ts-repo-utils/src/cmd/gen-index-ts.mts ./src --index-ext .mts --export-ext .mjs --target-ext .mts --target-ext .tsx --exclude entry-point.mts",
123
+ "lint": "eslint .",
124
+ "lint:fix": "eslint . --fix",
125
+ "prepare-release": "run-s fmt build doc",
126
+ "run:test": "tsx --tsconfig ../../tools/configs/tsconfig.tsx.json ./scripts/test-code/transform-test-code.mts",
127
+ "test": "pnpm run z:vitest:node run",
128
+ "test:browser": "pnpm run z:vitest --project='Browser' run",
129
+ "test:cov": "pnpm run z:vitest:node run --coverage",
130
+ "test:cov:ui": "vite preview --outDir ./coverage",
131
+ "test:ui": "pnpm run z:vitest:node --ui",
132
+ "testw": "pnpm run z:vitest:node watch",
133
+ "tsc": "node ../../node_modules/typescript-native/bin/tsc --noEmit",
134
+ "tscw": "node ../../node_modules/typescript-native/bin/tsc --noEmit --watch -p ./tsconfig.json",
135
+ "type-check": "node ../../node_modules/typescript-native/bin/tsc --noEmit",
136
+ "z:vitest": "vitest --config ./configs/vitest.config.mts",
137
+ "z:vitest:node": "pnpm run z:vitest --project='Node.js'"
184
138
  }
185
- }
139
+ }
@@ -8,7 +8,12 @@ import {
8
8
  pipe,
9
9
  } from 'ts-data-forge';
10
10
  import * as tsm from 'ts-morph';
11
- import { type DeepReadonly, type SafeUintWithSmallInt } from 'ts-type-forge';
11
+ import {
12
+ type DeepReadonly,
13
+ type SafeUintWithSmallInt,
14
+ type StrictExclude,
15
+ type StrictExtract,
16
+ } from 'ts-type-forge';
12
17
  import {
13
18
  hasDisableNextLineComment,
14
19
  isAtomicTypeNode,
@@ -397,7 +402,7 @@ const transformNode = (
397
402
  const transformTypeReferenceNode = (
398
403
  // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
399
404
  node: tsm.TypeReferenceNode,
400
- readonlyContext: Exclude<
405
+ readonlyContext: StrictExclude<
401
406
  ReadonlyContext,
402
407
  Readonly<{ type: 'readonly'; indexedAccessDepth: SafeUintWithSmallInt }>
403
408
  >,
@@ -813,7 +818,7 @@ const transformRestTypeNode = (
813
818
  const transformTypeOperatorNode = (
814
819
  // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
815
820
  node: tsm.TypeOperatorTypeNode,
816
- readonlyContext: Exclude<
821
+ readonlyContext: StrictExclude<
817
822
  ReadonlyContext,
818
823
  Readonly<{ type: 'readonly'; indexedAccessDepth: SafeUintWithSmallInt }>
819
824
  >,
@@ -852,7 +857,7 @@ const transformTypeOperatorNode = (
852
857
  const transformTypeLiteralNode = (
853
858
  // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
854
859
  node: tsm.TypeLiteralNode,
855
- readonlyContext: Exclude<
860
+ readonlyContext: StrictExclude<
856
861
  ReadonlyContext,
857
862
  Readonly<{ type: 'readonly'; indexedAccessDepth: SafeUintWithSmallInt }>
858
863
  >,
@@ -927,7 +932,7 @@ const transformIndexedAccessTypeNode = (
927
932
  const transformMappedTypeNode = (
928
933
  // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
929
934
  node: tsm.MappedTypeNode,
930
- readonlyContext: Exclude<
935
+ readonlyContext: StrictExclude<
931
936
  ReadonlyContext,
932
937
  Readonly<{ type: 'readonly'; indexedAccessDepth: SafeUintWithSmallInt }>
933
938
  >,
@@ -1109,7 +1114,7 @@ const transformConstructorParameter = (
1109
1114
  const transformIntersectionTypeNode = (
1110
1115
  // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
1111
1116
  node: tsm.IntersectionTypeNode,
1112
- readonlyContext: Exclude<
1117
+ readonlyContext: StrictExclude<
1113
1118
  ReadonlyContext,
1114
1119
  Readonly<{ type: 'readonly'; indexedAccessDepth: SafeUintWithSmallInt }>
1115
1120
  >,
@@ -1125,7 +1130,7 @@ const transformIntersectionTypeNode = (
1125
1130
  const transformUnionTypeNode = (
1126
1131
  // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
1127
1132
  node: tsm.UnionTypeNode,
1128
- readonlyContext: Exclude<
1133
+ readonlyContext: StrictExclude<
1129
1134
  ReadonlyContext,
1130
1135
  Readonly<{ type: 'readonly'; indexedAccessDepth: SafeUintWithSmallInt }>
1131
1136
  >,
@@ -1137,7 +1142,7 @@ const transformUnionTypeNode = (
1137
1142
  const transformUnionOrIntersectionTypeNodeImpl = (
1138
1143
  // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
1139
1144
  node: tsm.IntersectionTypeNode | tsm.UnionTypeNode,
1140
- readonlyContext: Exclude<
1145
+ readonlyContext: StrictExclude<
1141
1146
  ReadonlyContext,
1142
1147
  Readonly<{ type: 'readonly'; indexedAccessDepth: SafeUintWithSmallInt }>
1143
1148
  >,
@@ -1346,7 +1351,7 @@ const transformMembers = (
1346
1351
  // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
1347
1352
  members: readonly tsm.TypeElementTypes[],
1348
1353
  readonlyModifier: 'add' | 'remove',
1349
- readonlyContext: Extract<
1354
+ readonlyContext: StrictExtract<
1350
1355
  ReadonlyContext,
1351
1356
  Readonly<{
1352
1357
  type: 'DeepReadonly' | 'none';
@@ -1387,8 +1392,6 @@ const transformMembers = (
1387
1392
  }
1388
1393
 
1389
1394
  transformNode(mb, readonlyContext, options);
1390
-
1391
- continue;
1392
1395
  }
1393
1396
  };
1394
1397
 
@@ -1396,7 +1399,7 @@ const transformPropertySignature = (
1396
1399
  // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
1397
1400
  node: tsm.PropertySignature,
1398
1401
  readonlyModifier: 'add' | 'remove',
1399
- readonlyContext: Extract<
1402
+ readonlyContext: StrictExtract<
1400
1403
  ReadonlyContext,
1401
1404
  Readonly<{
1402
1405
  type: 'DeepReadonly' | 'none';
@@ -1406,8 +1409,8 @@ const transformPropertySignature = (
1406
1409
  options: ReadonlyTransformerOptionsInternal,
1407
1410
  ): void => {
1408
1411
  if (
1409
- readonlyContext.type === 'DeepReadonly' ||
1410
- readonlyModifier === 'remove'
1412
+ readonlyModifier === 'remove' ||
1413
+ readonlyContext.type === 'DeepReadonly'
1411
1414
  ) {
1412
1415
  node.setIsReadonly(false);
1413
1416
  } else {
@@ -1427,7 +1430,7 @@ const transformIndexSignatureDeclaration = (
1427
1430
  // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
1428
1431
  node: tsm.IndexSignatureDeclaration,
1429
1432
  readonlyModifier: 'add' | 'remove',
1430
- readonlyContext: Extract<
1433
+ readonlyContext: StrictExtract<
1431
1434
  ReadonlyContext,
1432
1435
  Readonly<{
1433
1436
  type: 'DeepReadonly' | 'none';
@@ -1443,8 +1446,8 @@ const transformIndexSignatureDeclaration = (
1443
1446
  }
1444
1447
 
1445
1448
  if (
1446
- readonlyContext.type === 'DeepReadonly' ||
1447
- readonlyModifier === 'remove'
1449
+ readonlyModifier === 'remove' ||
1450
+ readonlyContext.type === 'DeepReadonly'
1448
1451
  ) {
1449
1452
  // node.setIsReadonly(false);
1450
1453
  node.toggleModifier('readonly', false);
@@ -1,5 +1,5 @@
1
1
  import { SafeUint, match } from 'ts-data-forge';
2
- import { type SafeUintWithSmallInt } from 'ts-type-forge';
2
+ import { type SafeUintWithSmallInt, type StrictExtract } from 'ts-type-forge';
3
3
 
4
4
  /**
5
5
  * Controls whether to make a layer mutable during recursive transformation
@@ -37,7 +37,7 @@ export const nextReadonlyContext = <
37
37
  indexedAccessDepthChange: 'decr' | 'incr' | 'keep' | 'infinity';
38
38
  }
39
39
  | {
40
- nextReadonlyContextType: Extract<
40
+ nextReadonlyContextType: StrictExtract<
41
41
  ReadonlyContext['type'],
42
42
  'DeepReadonly'
43
43
  >;
@@ -47,7 +47,7 @@ export const nextReadonlyContext = <
47
47
  currentReadonlyContext: Curr;
48
48
  }
49
49
  >): Readonly<{
50
- type: Extract<ReadonlyContext['type'], 'DeepReadonly'> | Next;
50
+ type: Next | StrictExtract<ReadonlyContext['type'], 'DeepReadonly'>;
51
51
  indexedAccessDepth: SafeUintWithSmallInt;
52
52
  }> =>
53
53
  curr.type === 'DeepReadonly'
@@ -58,7 +58,7 @@ const shouldSkipFile = (
58
58
 
59
59
  const hasFileIgnoreComment = patterns.some((regex) => regex.test(code));
60
60
 
61
- if (Arr.isEmpty(ignoredTransformers) && !hasFileIgnoreComment) {
61
+ if (!hasFileIgnoreComment && Arr.isEmpty(ignoredTransformers)) {
62
62
  return false;
63
63
  }
64
64
 
@@ -1,6 +1,6 @@
1
1
  import { Arr, expectType } from 'ts-data-forge';
2
2
  import * as tsm from 'ts-morph';
3
- import { type DeepReadonly } from 'ts-type-forge';
3
+ import { type DeepReadonly, type StrictOmit } from 'ts-type-forge';
4
4
  import { isAtomicTypeNode } from './is-atomic-type-node.mjs';
5
5
 
6
6
  export const isShallowReadonlyTypeNode = (node: tsm.Node): boolean =>
@@ -17,7 +17,7 @@ export const isReadonlyTupleOrArrayTypeNode = (
17
17
  node.getTypeNode().isKind(tsm.SyntaxKind.TupleType));
18
18
 
19
19
  export type ReadonlyArrayTypeNode = tsm.TypeNode &
20
- Omit<tsm.TypeOperatorTypeNode, 'getOperator' | 'getTypeNode'> &
20
+ StrictOmit<tsm.TypeOperatorTypeNode, 'getOperator' | 'getTypeNode'> &
21
21
  Readonly<{
22
22
  getOperator: () => tsm.SyntaxKind.ReadonlyKeyword;
23
23
  getTypeNode: () => tsm.ArrayTypeNode;
@@ -91,7 +91,7 @@ if (import.meta.vitest !== undefined) {
91
91
  }
92
92
 
93
93
  export type ReadonlyTupleTypeNode = tsm.TypeNode &
94
- Omit<tsm.TypeOperatorTypeNode, 'getOperator' | 'getTypeNode'> &
94
+ StrictOmit<tsm.TypeOperatorTypeNode, 'getOperator' | 'getTypeNode'> &
95
95
  Readonly<{
96
96
  getOperator: () => tsm.SyntaxKind.ReadonlyKeyword;
97
97
  getTypeNode: () => tsm.TupleTypeNode;
@@ -153,9 +153,9 @@ if (import.meta.vitest !== undefined) {
153
153
  }
154
154
 
155
155
  export type ReadonlyTypeReferenceNode = tsm.TypeNode &
156
- Omit<tsm.TypeReferenceNode, 'getTypeName' | 'getTypeArguments'> &
156
+ StrictOmit<tsm.TypeReferenceNode, 'getTypeName' | 'getTypeArguments'> &
157
157
  Readonly<{
158
- getTypeName: () => Omit<tsm.Identifier, 'getText'> &
158
+ getTypeName: () => StrictOmit<tsm.Identifier, 'getText'> &
159
159
  Readonly<{
160
160
  getText: () => 'Readonly';
161
161
  }>;
@@ -23,10 +23,6 @@ export const shouldAvoidParenthesesForReadonly = (node: tsm.Node): boolean => {
23
23
  // Check if parent is TypePredicate: x is T
24
24
  // In this case, we MUST avoid parentheses: x is readonly T (not x is (readonly T))
25
25
  // because `x is (readonly T)` is a syntax error
26
- if (parent.isKind(tsm.SyntaxKind.TypePredicate)) {
27
- return true;
28
- }
29
-
30
26
  // For all other cases, keep parentheses for safety
31
- return false;
27
+ return parent.isKind(tsm.SyntaxKind.TypePredicate);
32
28
  };
@@ -1,3 +0,0 @@
1
- #!/usr/bin/env node
2
- export {};
3
- //# sourceMappingURL=append-as-const.d.mts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"append-as-const.d.mts","sourceRoot":"","sources":["../../src/cmd/append-as-const.mts"],"names":[],"mappings":""}