{"id":15663323,"url":"https://github.com/carlosmiei/ast-transpiler","last_synced_at":"2025-03-10T04:30:48.632Z","repository":{"id":65186039,"uuid":"548429247","full_name":"carlosmiei/ast-transpiler","owner":"carlosmiei","description":"AST Transpiler that converts Typescript into different languages (PHP, Python, C# (wip))","archived":true,"fork":false,"pushed_at":"2023-11-04T21:17:12.000Z","size":9556,"stargazers_count":20,"open_issues_count":0,"forks_count":11,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-02-13T07:33:52.946Z","etag":null,"topics":["ast","javascript","php","python","transpilation","transpiler","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/carlosmiei.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-10-09T14:52:22.000Z","updated_at":"2024-11-24T00:00:20.000Z","dependencies_parsed_at":"2023-01-12T11:30:42.849Z","dependency_job_id":null,"html_url":"https://github.com/carlosmiei/ast-transpiler","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/carlosmiei%2Fast-transpiler","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/carlosmiei%2Fast-transpiler/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/carlosmiei%2Fast-transpiler/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/carlosmiei%2Fast-transpiler/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/carlosmiei","download_url":"https://codeload.github.com/carlosmiei/ast-transpiler/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":242789155,"owners_count":20185397,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ast","javascript","php","python","transpilation","transpiler","typescript"],"created_at":"2024-10-03T13:36:37.830Z","updated_at":"2025-03-10T04:30:48.332Z","avatar_url":"https://github.com/carlosmiei.png","language":"TypeScript","funding_links":[],"categories":["Other Language Targets"],"sub_categories":[],"readme":"# AST-Transpiler\n## [Inactive] This repository was moved to https://github.com/ccxt/ast-transpiler\n[![Build](https://github.com/carlosmiei/ast-transpiler/actions/workflows/node.js.yml/badge.svg?branch=master)](https://github.com/carlosmiei/ast-transpiler/actions/workflows/node.js.yml)\n![Jest coverage](./badges/coverage-jest%20coverage.svg)\n![Functions](./badges/coverage-functions.svg)\n![Lines](./badges/coverage-lines.svg)\n![Statements](./badges/coverage-statements.svg)\n\n**⚠️ WORK IN PROGRESS** \n\n`ast-transpiler` is a library that allows transpiling typescript code to different languages using typescript's abstract syntax tree (AST) and type checker in an easy way abstracting most of the complexity behind the process.\n\n### [Install](#-installation) · [Usage](#-usage) · [Languages](#-languages) · [Options](#-options) · [Overrides](#-overrides) · [Examples](https://github.com/carlosmiei/ast-transpiler/tree/master/examples) \n\nAs expected, it's not possible to transpile Typescript to Python or PHP in a 1:1 parity because they are different languages a lot of features are not interchangeable. Nonetheless, this library supports as many features as possible, doing some adaptions (more to come).\n\nAlthough we transpile TS code directly to the other languages, this library does not touch import or exports statements because each language has its own module/namespace model. Instead, we return a unified list of imports and exports separately, allowing the user to adapt it to the target language easily and append it to the generated code (check `IFileImport` and `IFileExport`).\n\nIn order to facilitate the transpilation process, we should try to add as many types as possible otherwise, we might get invalid results.\n\n**❌ Bad Example**\n```Javascript\nfunction importantFunction(argument) { // type of argumment is unknown\n    const length = argument.length;\n}\n```\n\n⬆️ In this case, we have no means to infer the argument's type, so for instance in PHP we don't know if `.length` should be transpiled to `str_len` or `count`.\n\n\n**✅ Good Example**\n\n```Javascript\nfunction importantFunction(argument: string[]) {\n    const length = argument.length;\n}\n```\n⬆️ argument's type is known so all good, no ambiguities here.\n\n\n**❌ Bad Example (C# only)**\n```Javascript\nclass x {\n    myMethod () {\n        let i = 1;\n        i = \"string\";\n    }\n}\n```\nUnlike other languages, C# does not allow changing the type of a variable/rebinding the same name to a different type (we don't use `dynamic` values because of the performance impact). So, if you intend to transpile to C# avoid this pattern.\n\n#### What about javascript?\nObviously, all Javascript code is valid Typescript, so in theory, it should transpile Javascript seamlessly as well. This is in part true, but for the lacking of types, we might get some invalid results when the types are not clear (check bad example).\n\n#### ESM or CJS?\nThis library works better with ESM because has dedicated `import/export` tokens in the AST whereas CJS `require/module.exports` are just regular properties and call expressions. **Nonetheless, both are supported**.\n\n## 🎯 Languages\nCurrently the following languages are supported:\n- Python\n- PHP\n- C# (WIP)\n\n## 🔌 Installation\n\nUse the package manager [npm](https://www.npmjs.com/) to install foobar.\n\n```bash\nnpm install ast-transpiler\n```\n\n## 📌 Usage\n\n`ast-transpiler` is a hybrid package, supporting ESM and CJS out of the box. Choose the one that fits you better.\n\n### Transpiling Typescript to Python from string\n```Javascript \nimport Transpiler from 'ast-transpiler'\n\nconst transpiler = new Transpiler({\n    python: {\n        uncamelcaseIdentifiers: true, \n    }\n});\n\nconst ts = \"const myVar = 1;\"\nconst transpiledCode = transpiler.transpilePython(ts);\n\nconsole.log(transpileCode.content) // prints my_var = 1\n```\n\n### Transpiling Typescript to PHP from file\n(preferred way if needs to resolve imports)\n\n```Javascript\nconst Transpiler = require(`ast-transpiler`);\n\nconst transpiler = new Transpiler();\nconst transpiledCode = transpiler.transpilePhpByPath(\"./my/path/file.ts\");\n\nconsole.log(transpiler.content) // prints transpiled php\nconsole.log(transpiler.imports) // prints unified imports statements if any\nconsole.log(transpiler.exports) // prints unified export statements if any\n```\n\n## ⚡ C# Notes\n### Helpers\nC# is very different from languages like Typescript, Python or PHP since it's statically typed and much more restricted than the others mentioned. Things like falsy values, empty default objects, dynamic properties, different type comparison, untyped arguments/return type, etc do not exist so I had to create a set of wrappers that will emulate these features in C#. So in order to make your code run you need to make all the **helper** methods available [here](https://github.com/carlosmiei/ast-transpiler/blob/c%23/helpers/c%23/helpers.cs) accessible from your code (wip).\n\n\n### Mandatory return type/parameter type\nAs you probably know c# requires you to define the type for every parameter and method declaration whereas Typescript/Javascript does not, so this package will try to infer the type if not available or default to `object`, so it's preferable to declare them to avoid errors.\n\n### Number ambiguity\nUnfortunately Typescript/Javascript has only one type, `Number` which represents both integers and floating-point numbers. This is problematic because C# offers a variety of types (uint, int, Int64, double, float) to represent numeric values so it's very hard if not impossible to correctly transpile `Number`. For now we're converting it to `object` .\n\n### Json Objects\nUnlike Ts, Py, PHP, we don't have a direct way to represent a JSON object, so I try to represent it using either a `Dictionary\u003cstring, object\u003e` or `List\u003cobject\u003e` \n\nWarning: Under active development so can change at any time!\n\n## ✅ Supported Features\n- Identation\n  - Does not rely on the original indentation but on the hierarchy of the statements, can be controlled by setting `DEFAULT_IDENTATION` (default value is four spaces)\n- Variable declarations\n- Class/function/methods declarations\n- For/While loops\n- Basic string manipulation \n  - `concat`, `length`, `includes`, `indexOf`\n- Basic arrays manipulation\n  - `includes`, `length`, `push`, `pop`, `shift`\n- Basic object manipulation\n  - `Object.keys`, `Object.values`\n- Binary expressions\n  - `+,-,*,/,mod` \n- Condition expressions\n  - `\u0026\u0026, ||`\n- Basic math functions\n  - `Math.min, Math.max, Math.floor, Math.ceil, parseFloat, parseInt`\n- Basic JSON methods\n  - `JSON.stringify, JSON.parse` \n- Throw statements\n- Conditional Expressions\n- Break expressions\n- Basic instanceof statements\n- Comments\n  - ⚠️ Some comments are not available in the AST, so those are lost\n- Snake casing of variables/calls/functions/methods\n- Import/Export statements parsing (ESM/CJS)\n  - ⚠️ Avoid complex CJS exports\n- Basic async support (async methods/functions, await, promise.all)\n    - ⚠️ PHP: By default it uses the `ReactPHP` approach\n- Scope Resolution Operator conversion (PHP only)\n- etc\n\nWe will try to add more features/conversions in the future but this process is also customizable, check the Overrides section.\n\n## 🔧 Options\n\nAs mentioned above, this library allows for some customization through the offered options and available overrides.\n\nCurrently there are two generic boolean transpiling options, `uncamelcaseIdentifiers` and `asyncTranspiling`. As the name suggests the former defines if all identifiers (variables, methods, functions, expression calls) should uncamelcased and the latter if we want our transpiled code to be async. \n\nYou can also turn warn the warnings by setting the `verbose` option.\n\nThey can be set upon instantiating our transpiler, or using setter methods\n\n```Javascript\nconst transpiler = new Transpiler({\n        verbose: true\n    python: {\n        uncamelcaseIdentifiers: false, // default value\n        asyncTranspiling: true // default value\n    },\n    php:  {\n        uncamelcaseIdentifiers: false, // default value\n        asyncTranspiling: true // default value\n    }\n});\n\n// Alternatively\ntranspiler.setPhpUncamelCaseIdentifiers(true);\ntranspiler.setPythonAsyncTranspiling(false);\n```\n\n## 🔨 Overrides\n\nThere is no perfect recipe for transpiling one language in another completely different so we have to made some choices that you might not find the most correct or might want to change it slightly. For that reason this library exposes some objects and methods that you might load up with your own options.\n### Parser\n\nThis object contains all tokens used to convert one language into another (if token, return token, while token, etc). Let's say that you prefer the `array()` notation instead of the default `[]` syntax. You can easily do that by overriding the  `ARRAY_OPENING_TOKEN` and `ARRAY_CLOSING_TOKEN`. You can check all available tokens [here](https://github.com/carlosmiei/ast-transpiler/blob/master/dist/transpiler.d.ts#L19)\n\nExample:\n\n```Javascript\nconst customParserConfig = {\n    'ARRAY_OPENING_TOKEN': 'array(',\n    'ARRAY_CLOSING_TOKEN': ')'\n}\n\nconst config = {\n    \"php\": {\n        \"parser\": customParserConfig\n    }\n}\nconst transpiler = new Transpiler(config)\n```\n\n### FullPropertyAccessReplacements\n\nBy default this library will literally convert property access expressions, for instance `myVar.x`  will be converted to `myVar.x` in python but there are certain properties we want map to a different value in order to preserve functionality. In python we don't want to use `console.log` to print a message, so we need to convert this property to `print`. `FullPropertyAccessReplacements` contains all of those property conversions.  So if we want to convert `JSON.parse` to `json.loads` we just need to add it here (this particular conversion is done by default so you don't need to add it manually).\n\n```Javascript\n\nconst customFullPropertyAccessReplacements = {\n    'JSON.parse': 'json.loads',\n}\n\nconst config = {\n    \"python\": {\n        \"FullPropertyAccessReplacements\": customParserConfig\n    }\n}\n```\n\n#### LeftPropertyAccessReplacements\n- Same logic as for `FullPropertyAccessReplacements` but we should use this object when we want to replace the `left` side only. This is useful for mapping `this` to the correspondent value in the other language, but you might want to customize it as well.\n\n```Javascript\nconst LeftPropertyAccessReplacements = {\n    'this': 'self',\n}\n\nconst config = {\n    \"python\": {\n        \"LeftPropertyAccessReplacements\": LeftPropertyAccessReplacements\n    }\n}\n// this.x will be converted to self.x\n```\n#### RightPropertyAccessReplacements\n- Same story as for `FullPropertyAccessReplacements` but only replaces the `right` side.\n\n```Javascript\nconst customRightPropertyAccessReplacements = {\n    'toUpperCase': 'upper',\n}\n\nconst config = {\n    \"python\": {\n        \"RightPropertyAccessReplacements\": customRightPropertyAccessReplacements\n    }\n}\n// x.toUpperCase() will be converted to x.upper()\n```\n\n#### CallExpressionReplacements\nSimilar to `FullPropertyAccessReplacements` but applies to expression calls only. \n\n\n```Javascript\nconst CallExpressionReplacements = {\n    'parseInt': 'float',\n}\n\nconst config = {\n    \"python\": {\n        \"CallExpressionReplacements\": CallExpressionReplacements\n    }\n}\n// parseInt(\"1\") will be converted to float(\"1\")\n```\n\n#### StringLiteralReplacements\nSimilar to `FullPropertyAccessReplacements` but applies to string literals\n\n\n```Javascript\nconst StringLiteralReplacements = {\n    'sha256': 'hashlib.sha256',\n}\n\nconst config = {\n    \"python\": {\n        \"StringLiteralReplacements\": StringLiteralReplacements\n    }\n}\n// \"sha256\" will be converted to hashlib.sha256\n```\n\n#### ReservedKeywordsReplacements\nLanguages like C# have a lot of reserved words (string, object, params, base, internal, event, etc) so you can use this object to add your replacements.\n\n#### ScopeResolutionProps (PHP only)\nIn PHP, there is the *Scope Resolution Operation* that allows access to *static/constant/overridden* properties, so in these cases, we must use a different property access token. Since this concept does not exist in typescript, we have to rely on a list of properties provided by the user where the `::` operator should be applied.\n\n\n```Javascript\nconst ScopeResolutionProps = [\n    'Precise'\n]\n\nconst config = {\n    \"php\": {\n        \"ScopeResolutionProps\": ScopeResolutionProps\n    }\n}\n\n// Precise.string() will be converted to Precise::string()\n```\n\n#### Methods\n\nDue to the nature of this process, there are a lot of things that can't be transpiled by direct replacements so we need to add custom logic depending on the target language. For that reason, there are a lot of small atomic methods that can be overridden to add custom modifications.\n\n##### Example 1: Removing all comments from PHP code\n\n```Javascript\n\nconst transpiler = new Transpiler();\n\nfunction myPrintFunctionComment (comment) {\n    return \"\";\n}\n\ntranspiler.phpTranspiler.transformLeadingComment = myPrintFunctionComment;\ntranspiler.phpTranspiler.transformTrailingComment = myPrintFunctionComment;\n```\n\n##### Example 2: Custom call expression modification in Python\n\n```Javascript\nconst transpiler = new Transpiler();\n\nfunction printOutOfOrderCallExpressionIfAny(node, identation) {\n    const expressionText = node.expression.getText();\n    const args = node.arguments;\n    if (expressionText === \"Array.isArray\") {\n        return \"isinstance(\" + this.printNode(args[0], 0) + \", list)\"; // already done out of the box so no need to add it\n    }\n\n    return super.printOutOfOrderCallExpressionIfAny(node, identation); // avoid interfering with the builtn modifications\n}\n\ntranspiler.pythonTranspiler.printOutOfOrderCallExpressionIfAny = printOutOfOrderCallExpressionIfAny;\n```\n\n## Contributing\n\nPull requests are welcome. For major changes, please open an issue first\nto discuss what you would like to change.\n\nPlease make sure to update tests as appropriate.\n\n## License\n\n[MIT](https://choosealicense.com/licenses/mit/)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcarlosmiei%2Fast-transpiler","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcarlosmiei%2Fast-transpiler","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcarlosmiei%2Fast-transpiler/lists"}