ts-codemod-lib 2.2.3 → 2.2.5
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.d.mts.map +1 -1
- package/dist/functions/ast-transformers/convert-to-readonly.mjs +12 -13
- package/dist/functions/ast-transformers/convert-to-readonly.mjs.map +1 -1
- package/dist/functions/ast-transformers/readonly-transformer-helpers/readonly-context.d.mts +3 -3
- package/dist/functions/ast-transformers/readonly-transformer-helpers/readonly-context.d.mts.map +1 -1
- package/dist/functions/ast-transformers/readonly-transformer-helpers/readonly-context.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.d.mts +5 -5
- package/dist/functions/functions/is-readonly-node.d.mts.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/dist/functions/functions/should-avoid-parentheses-for-readonly.d.mts.map +1 -1
- package/dist/functions/functions/should-avoid-parentheses-for-readonly.mjs +1 -4
- package/dist/functions/functions/should-avoid-parentheses-for-readonly.mjs.map +1 -1
- package/package.json +37 -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 +31 -33
- package/src/functions/ast-transformers/readonly-transformer-helpers/readonly-context.mts +3 -3
- 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 +6 -6
- package/src/functions/functions/should-avoid-parentheses-for-readonly.mts +1 -5
|
@@ -41,10 +41,10 @@ const shouldSkipFile = (code, transformerName) => {
|
|
|
41
41
|
/\/\*\s*transform-ignore\s*.*?\s*\*\//u
|
|
42
42
|
];
|
|
43
43
|
const hasFileIgnoreComment = patterns.some((regex) => regex.test(code));
|
|
44
|
-
if (Arr.
|
|
44
|
+
if (!hasFileIgnoreComment && Arr.isEmpty(ignoredTransformers)) {
|
|
45
45
|
return false;
|
|
46
46
|
}
|
|
47
|
-
if (Arr.
|
|
47
|
+
if (Arr.isEmpty(ignoredTransformers)) {
|
|
48
48
|
return true;
|
|
49
49
|
}
|
|
50
50
|
if (transformerName === void 0) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transform-source-code.mjs","sources":["../../../src/functions/ast-transformers/transform-source-code.mts"],"sourcesContent":["import { Arr } from 'ts-data-forge';\nimport * as tsm from 'ts-morph';\nimport { type TsMorphTransformer } from './types.mjs';\n\nconst extractFileIgnoreTransformers = (code: string): readonly string[] => {\n // Try to find any of the supported file-level ignore comment prefixes\n const patterns = [\n {\n prefix: 'transformer-ignore',\n regex: /\\/\\*\\s*transformer-ignore\\s*(.*?)\\s*\\*\\//u,\n },\n {\n prefix: 'ts-codemod-ignore',\n regex: /\\/\\*\\s*ts-codemod-ignore\\s*(.*?)\\s*\\*\\//u,\n },\n {\n prefix: 'codemod-ignore',\n regex: /\\/\\*\\s*codemod-ignore\\s*(.*?)\\s*\\*\\//u,\n },\n {\n prefix: 'transform-ignore',\n regex: /\\/\\*\\s*transform-ignore\\s*(.*?)\\s*\\*\\//u,\n },\n ] as const;\n\n for (const { regex } of patterns) {\n const match = regex.exec(code);\n\n if (match !== null) {\n const targetTransformers = match[1]?.trim() ?? '';\n\n // Empty means ignore all transformers\n if (targetTransformers === '') {\n return [];\n }\n\n // Parse comma-separated transformer names\n return targetTransformers.split(',').map((name) => name.trim());\n }\n }\n\n return [];\n};\n\nconst shouldSkipFile = (\n code: string,\n transformerName: string | undefined,\n): boolean => {\n const ignoredTransformers = extractFileIgnoreTransformers(code);\n\n // If no file-level ignore comment found, don't skip\n const patterns = [\n /\\/\\*\\s*transformer-ignore\\s*.*?\\s*\\*\\//u,\n /\\/\\*\\s*ts-codemod-ignore\\s*.*?\\s*\\*\\//u,\n /\\/\\*\\s*codemod-ignore\\s*.*?\\s*\\*\\//u,\n /\\/\\*\\s*transform-ignore\\s*.*?\\s*\\*\\//u,\n ] as const;\n\n const hasFileIgnoreComment = patterns.some((regex) => regex.test(code));\n\n if (Arr.
|
|
1
|
+
{"version":3,"file":"transform-source-code.mjs","sources":["../../../src/functions/ast-transformers/transform-source-code.mts"],"sourcesContent":["import { Arr } from 'ts-data-forge';\nimport * as tsm from 'ts-morph';\nimport { type TsMorphTransformer } from './types.mjs';\n\nconst extractFileIgnoreTransformers = (code: string): readonly string[] => {\n // Try to find any of the supported file-level ignore comment prefixes\n const patterns = [\n {\n prefix: 'transformer-ignore',\n regex: /\\/\\*\\s*transformer-ignore\\s*(.*?)\\s*\\*\\//u,\n },\n {\n prefix: 'ts-codemod-ignore',\n regex: /\\/\\*\\s*ts-codemod-ignore\\s*(.*?)\\s*\\*\\//u,\n },\n {\n prefix: 'codemod-ignore',\n regex: /\\/\\*\\s*codemod-ignore\\s*(.*?)\\s*\\*\\//u,\n },\n {\n prefix: 'transform-ignore',\n regex: /\\/\\*\\s*transform-ignore\\s*(.*?)\\s*\\*\\//u,\n },\n ] as const;\n\n for (const { regex } of patterns) {\n const match = regex.exec(code);\n\n if (match !== null) {\n const targetTransformers = match[1]?.trim() ?? '';\n\n // Empty means ignore all transformers\n if (targetTransformers === '') {\n return [];\n }\n\n // Parse comma-separated transformer names\n return targetTransformers.split(',').map((name) => name.trim());\n }\n }\n\n return [];\n};\n\nconst shouldSkipFile = (\n code: string,\n transformerName: string | undefined,\n): boolean => {\n const ignoredTransformers = extractFileIgnoreTransformers(code);\n\n // If no file-level ignore comment found, don't skip\n const patterns = [\n /\\/\\*\\s*transformer-ignore\\s*.*?\\s*\\*\\//u,\n /\\/\\*\\s*ts-codemod-ignore\\s*.*?\\s*\\*\\//u,\n /\\/\\*\\s*codemod-ignore\\s*.*?\\s*\\*\\//u,\n /\\/\\*\\s*transform-ignore\\s*.*?\\s*\\*\\//u,\n ] as const;\n\n const hasFileIgnoreComment = patterns.some((regex) => regex.test(code));\n\n if (!hasFileIgnoreComment && Arr.isEmpty(ignoredTransformers)) {\n return false;\n }\n\n // Empty array means ignore all transformers (file-level ignore without specific transformers)\n if (Arr.isEmpty(ignoredTransformers)) {\n return true;\n }\n\n // If transformer name is not specified, don't skip\n if (transformerName === undefined) {\n return false;\n }\n\n // Check if the transformer is in the ignore list\n return ignoredTransformers.includes(transformerName);\n};\n\nexport const transformSourceCode = (\n code: string,\n isTsx: boolean,\n transformers: readonly TsMorphTransformer[],\n debug: boolean = false,\n): string => {\n const project = new tsm.Project({\n useInMemoryFileSystem: true,\n compilerOptions: {\n jsx: isTsx ? tsm.ts.JsxEmit.React : undefined,\n target: tsm.ts.ScriptTarget.ESNext,\n module: tsm.ts.ModuleKind.ESNext,\n },\n });\n\n const sourceAst = project.createSourceFile(\n `source.${isTsx ? 'tsx' : 'ts'}`,\n code,\n );\n\n for (const transformer of transformers) {\n const transformerName = transformer.name;\n\n if (shouldSkipFile(code, transformerName)) {\n if (debug) {\n console.debug(\n `skipped by ignore-file comment for transformer: ${transformerName}`,\n );\n }\n\n continue;\n }\n\n transformer.transform(sourceAst);\n }\n\n return sourceAst.getFullText();\n};\n"],"names":[],"mappings":"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"}
|
|
@@ -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;;;;"}
|
|
@@ -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 &
|
|
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 &
|
|
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 &
|
|
16
|
-
getTypeName: () =>
|
|
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;
|
|
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"}
|
|
@@ -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
|
|
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,
|
|
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
|
-
|
|
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
|
|
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.
|
|
3
|
+
"version": "2.2.5",
|
|
4
4
|
"private": false,
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typescript",
|
|
@@ -51,29 +51,29 @@
|
|
|
51
51
|
"agents:sync": "tsx ./scripts/cmd/sync-agent-config.mts",
|
|
52
52
|
"build": "tsx ./scripts/cmd/build.mts",
|
|
53
53
|
"build:min": "tsx ./scripts/cmd/build.mts --skip-check",
|
|
54
|
-
"check:ext": "tsx ./scripts/cmd/check-ext.mts",
|
|
55
54
|
"check-all": "tsx ./scripts/cmd/check-all.mts",
|
|
55
|
+
"check:ext": "tsx ./scripts/cmd/check-ext.mts",
|
|
56
56
|
"clean": "pnpm run /clean:.*/",
|
|
57
57
|
"clean:logs": "npx rimraf ./*.log",
|
|
58
58
|
"codemod": "run-s codemod:uncommitted:* fmt",
|
|
59
59
|
"codemod:diff": "run-s codemod:diff:* fmt",
|
|
60
|
-
"codemod:diff:as-const": "tsx ./src/cmd/append-as-const.mts --diff-from origin/main '{src,scripts,samples,test}/**/*.{mts,tsx}'",
|
|
61
60
|
"codemod:diff:any-to-unknown": "tsx ./src/cmd/replace-any-with-unknown.mts --diff-from origin/main '{src,scripts,samples,test}/**/*.{mts,tsx}'",
|
|
62
|
-
"codemod:diff:
|
|
61
|
+
"codemod:diff:as-const": "tsx ./src/cmd/append-as-const.mts --diff-from origin/main '{src,scripts,samples,test}/**/*.{mts,tsx}'",
|
|
63
62
|
"codemod:diff:interface-to-type": "tsx ./src/cmd/convert-interface-to-type.mts --diff-from origin/main '{src,scripts,samples,test}/**/*.{mts,tsx}'",
|
|
64
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
65
|
"codemod:full": "run-s codemod:full:* fmt",
|
|
66
|
-
"codemod:full:as-const": "tsx ./src/cmd/append-as-const.mts '{src,scripts,samples,test}/**/*.{mts,tsx}'",
|
|
67
66
|
"codemod:full:any-to-unknown": "tsx ./src/cmd/replace-any-with-unknown.mts '{src,scripts,samples,test}/**/*.{mts,tsx}'",
|
|
68
|
-
"codemod:full:
|
|
67
|
+
"codemod:full:as-const": "tsx ./src/cmd/append-as-const.mts '{src,scripts,samples,test}/**/*.{mts,tsx}'",
|
|
69
68
|
"codemod:full:interface-to-type": "tsx ./src/cmd/convert-interface-to-type.mts '{src,scripts,samples,test}/**/*.{mts,tsx}'",
|
|
70
69
|
"codemod:full:readonly": "tsx ./src/cmd/convert-to-readonly.mts '{src,scripts,samples,test}/**/*.{mts,tsx}'",
|
|
71
|
-
"codemod:
|
|
70
|
+
"codemod:full:unknown-record": "tsx ./src/cmd/replace-record-with-unknown-record.mts '{src,scripts,samples,test}/**/*.{mts,tsx}'",
|
|
72
71
|
"codemod:uncommitted:any-to-unknown": "tsx ./src/cmd/replace-any-with-unknown.mts --uncommitted '{src,scripts,samples,test}/**/*.{mts,tsx}'",
|
|
73
|
-
"codemod:uncommitted:
|
|
72
|
+
"codemod:uncommitted:as-const": "tsx ./src/cmd/append-as-const.mts --uncommitted '{src,scripts,samples,test}/**/*.{mts,tsx}'",
|
|
74
73
|
"codemod:uncommitted:interface-to-type": "tsx ./src/cmd/convert-interface-to-type.mts --uncommitted '{src,scripts,samples,test}/**/*.{mts,tsx}'",
|
|
75
74
|
"codemod:uncommitted:readonly": "tsx ./src/cmd/convert-to-readonly.mts --uncommitted '{src,scripts,samples,test}/**/*.{mts,tsx}'",
|
|
76
|
-
"
|
|
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
77
|
"doc": "tsx ./scripts/cmd/gen-docs.mts",
|
|
78
78
|
"doc:embed": "tsx ./scripts/cmd/embed-examples.mts",
|
|
79
79
|
"doc:preview": "vite preview --config ./configs/vite.doc.config.mts",
|
|
@@ -81,19 +81,14 @@
|
|
|
81
81
|
"fmt": "format-uncommitted",
|
|
82
82
|
"fmt:diff": "format-diff-from origin/main",
|
|
83
83
|
"fmt:full": "prettier --write .",
|
|
84
|
-
"gh:apply-all": "gh-apply-all",
|
|
85
|
-
"gh:apply-repository-settings": "gh-apply-repository-settings",
|
|
86
|
-
"gh:apply-rulesets": "gh-apply-rulesets",
|
|
87
|
-
"gh:apply-variables": "gh-apply-variables",
|
|
88
|
-
"gh:backup-all": "gh-backup-all",
|
|
89
|
-
"gh:backup-repository-settings": "gh-backup-repository-settings",
|
|
90
|
-
"gh:backup-rulesets": "gh-backup-rulesets",
|
|
91
84
|
"gi": "run-s gi:src fmt",
|
|
92
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",
|
|
93
86
|
"lint": "eslint .",
|
|
94
87
|
"lint:fix": "eslint . --fix",
|
|
95
88
|
"md": "markdownlint-cli2",
|
|
96
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",
|
|
97
92
|
"run:cmd": "tsx ./src/cmd/convert-to-readonly.mts ./test/sample1.mts",
|
|
98
93
|
"run:test": "tsx ./scripts/test-code/transform-test-code.mts",
|
|
99
94
|
"sync-cli-versions": "tsx scripts/cmd/sync-cli-versions.mts",
|
|
@@ -106,55 +101,57 @@
|
|
|
106
101
|
"tsc": "node ./node_modules/typescript-native/bin/tsc --noEmit",
|
|
107
102
|
"tscw": "node ./node_modules/typescript-native/bin/tsc --noEmit --watch -p ./tsconfig.json",
|
|
108
103
|
"type-check": "node ./node_modules/typescript-native/bin/tsc --noEmit",
|
|
109
|
-
"update-packages": "pnpm update --latest '!typescript'",
|
|
110
104
|
"z:vitest": "vitest --config ./configs/vitest.config.mts",
|
|
111
105
|
"z:vitest:node": "pnpm run z:vitest --project='Node.js'"
|
|
112
106
|
},
|
|
113
107
|
"dependencies": {
|
|
114
|
-
"ts-data-forge": "^
|
|
108
|
+
"ts-data-forge": "^14.1.0",
|
|
115
109
|
"ts-morph": "^28.0.0",
|
|
116
|
-
"ts-type-forge": "^
|
|
110
|
+
"ts-type-forge": "^9.1.3"
|
|
117
111
|
},
|
|
118
112
|
"devDependencies": {
|
|
119
113
|
"@rollup/plugin-replace": "6.0.3",
|
|
120
114
|
"@rollup/plugin-strip": "3.0.4",
|
|
121
|
-
"@semantic-release/changelog": "
|
|
115
|
+
"@semantic-release/changelog": "7.0.0",
|
|
122
116
|
"@semantic-release/commit-analyzer": "13.0.1",
|
|
123
117
|
"@semantic-release/exec": "7.1.0",
|
|
124
|
-
"@semantic-release/git": "
|
|
125
|
-
"@semantic-release/github": "12.0.
|
|
118
|
+
"@semantic-release/git": "11.0.1",
|
|
119
|
+
"@semantic-release/github": "12.0.9",
|
|
126
120
|
"@semantic-release/npm": "13.1.5",
|
|
127
121
|
"@semantic-release/release-notes-generator": "14.1.1",
|
|
128
|
-
"@types/node": "26.
|
|
122
|
+
"@types/node": "26.2.0",
|
|
129
123
|
"@vitest/browser-playwright": "4.1.10",
|
|
130
124
|
"@vitest/coverage-v8": "4.1.10",
|
|
131
|
-
"@vitest/ui": "4.1.
|
|
125
|
+
"@vitest/ui": "4.1.10",
|
|
132
126
|
"cmd-ts": "0.15.0",
|
|
133
127
|
"conventional-changelog-conventionalcommits": "10.2.1",
|
|
134
128
|
"cspell": "10.0.1",
|
|
135
129
|
"dedent": "1.7.2",
|
|
136
130
|
"esbuild": "0.28.1",
|
|
137
|
-
"eslint": "10.
|
|
138
|
-
"eslint-config-typed": "5.4
|
|
139
|
-
"
|
|
131
|
+
"eslint": "10.8.0",
|
|
132
|
+
"eslint-config-typed": "5.8.4",
|
|
133
|
+
"eslint-plugin-ts-data-forge": "0.4.2",
|
|
134
|
+
"eslint-plugin-ts-fortress": "1.0.0",
|
|
135
|
+
"eslint-plugin-ts-type-forge": "0.6.1",
|
|
136
|
+
"github-settings-as-code": "2.1.0",
|
|
140
137
|
"jiti": "2.7.0",
|
|
141
|
-
"markdownlint": "0.41.
|
|
142
|
-
"markdownlint-cli2": "0.23.
|
|
143
|
-
"npm-run-all2": "9.0.
|
|
144
|
-
"playwright": "1.
|
|
145
|
-
"prettier": "3.9.
|
|
138
|
+
"markdownlint": "0.41.1",
|
|
139
|
+
"markdownlint-cli2": "0.23.2",
|
|
140
|
+
"npm-run-all2": "9.0.3",
|
|
141
|
+
"playwright": "1.62.1",
|
|
142
|
+
"prettier": "3.9.6",
|
|
146
143
|
"prettier-plugin-organize-imports": "4.3.0",
|
|
147
144
|
"prettier-plugin-packagejson": "3.0.2",
|
|
148
|
-
"rollup": "4.62.
|
|
145
|
+
"rollup": "4.62.4",
|
|
149
146
|
"rollup-plugin-esbuild": "6.2.1",
|
|
150
|
-
"semantic-release": "25.0.
|
|
151
|
-
"ts-repo-utils": "10.1.
|
|
152
|
-
"tsx": "4.23.
|
|
153
|
-
"typedoc": "0.28.
|
|
147
|
+
"semantic-release": "25.0.8",
|
|
148
|
+
"ts-repo-utils": "10.1.7",
|
|
149
|
+
"tsx": "4.23.4",
|
|
150
|
+
"typedoc": "0.28.20",
|
|
154
151
|
"typedoc-github-theme": "0.4.0",
|
|
155
152
|
"typescript": "6.0.3",
|
|
156
|
-
"typescript-native": "npm:typescript@7.0.
|
|
157
|
-
"vite": "8.
|
|
153
|
+
"typescript-native": "npm:typescript@7.0.2",
|
|
154
|
+
"vite": "8.2.0",
|
|
158
155
|
"vitest": "4.1.10"
|
|
159
156
|
},
|
|
160
157
|
"peerDependencies": {
|
|
@@ -173,7 +170,7 @@
|
|
|
173
170
|
"optional": true
|
|
174
171
|
}
|
|
175
172
|
},
|
|
176
|
-
"packageManager": "pnpm@11.
|
|
173
|
+
"packageManager": "pnpm@11.20.0",
|
|
177
174
|
"engines": {
|
|
178
175
|
"node": ">=22.22.2",
|
|
179
176
|
"pnpm": ">=9.0.0"
|
|
@@ -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
|
|