Skip to content
2 changes: 2 additions & 0 deletions src/baseTranspiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { IFileImport, IFileExport, TranspilationError, IMethodType, IParameterType } from './types.js';
import { unCamelCase } from "./utils.js";
import { Logger } from "./logger.js";
import { timingSafeEqual } from 'crypto';

Check warning on line 5 in src/baseTranspiler.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

'timingSafeEqual' is defined but never used
class BaseTranspiler {

NUM_LINES_BETWEEN_CLASS_MEMBERS = 1;
Expand Down Expand Up @@ -190,6 +190,7 @@
uncamelcaseIdentifiers;
asyncTranspiling;
requiresReturnType;
supportVariableType;
requiresParameterType;
supportsFalsyOrTruthyValues;
requiresCallExpressionCast;
Expand All @@ -202,6 +203,7 @@
this.id = "base";
this.uncamelcaseIdentifiers = false;
this.requiresReturnType = false;
this.supportVariableType = false;
this.requiresParameterType = false;
this.supportsFalsyOrTruthyValues = true;
this.requiresCallExpressionCast = false;
Expand Down Expand Up @@ -342,7 +344,7 @@

let method = undefined;

let parentClass = (ts as any).getAllSuperTypeNodes(node.parent)[0];

Check warning on line 347 in src/baseTranspiler.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

Unexpected any. Specify a different type

while (parentClass !== undefined) {
const parentClassType = global.checker.getTypeAtLocation(parentClass);
Expand All @@ -359,13 +361,13 @@
if (ts.isMethodDeclaration(elem)) {

const name = elem.name.getText().trim();
if ((node as any).name.escapedText === name) {

Check warning on line 364 in src/baseTranspiler.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

Unexpected any. Specify a different type
method = elem;
}
}
});

parentClass = (ts as any).getAllSuperTypeNodes(parentClassDecl)[0] ?? undefined;

Check warning on line 370 in src/baseTranspiler.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

Unexpected any. Specify a different type
}


Expand All @@ -378,7 +380,7 @@
return this.DEFAULT_IDENTATION.repeat(parseInt(num));
}

getBlockOpen(identation){

Check warning on line 383 in src/baseTranspiler.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

'identation' is defined but never used
return this.SPACE_BEFORE_BLOCK_OPENING + this.BLOCK_OPENING_TOKEN + "\n";
}

Expand Down Expand Up @@ -443,11 +445,11 @@
return this.getIden(identation) + `${left} instanceof ${right}`;
}

getCustomOperatorIfAny(left, right, operator) {

Check warning on line 448 in src/baseTranspiler.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

'operator' is defined but never used

Check warning on line 448 in src/baseTranspiler.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

'right' is defined but never used

Check warning on line 448 in src/baseTranspiler.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

'left' is defined but never used
return undefined;
}

printCustomBinaryExpressionIfAny(node, identation) {

Check warning on line 452 in src/baseTranspiler.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

'identation' is defined but never used

Check warning on line 452 in src/baseTranspiler.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

'node' is defined but never used
return undefined; // stub to override
}

Expand Down
22 changes: 22 additions & 0 deletions src/phpTranspiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export class PhpTranspiler extends BaseTranspiler {
super(config);
this.id = "php";
this.asyncTranspiling = config['async'] ?? true;
this.supportVariableType = config['supportVariableType'] ?? false;
this.uncamelcaseIdentifiers = config['uncamelcaseIdentifiers'] ?? false;
this.removeVariableDeclarationForFunctionExpression = config['removeFunctionAssignToVariable'] ?? false;
this.includeFunctionNameInFunctionExpressionDeclaration = config['includeFunctionNameInFunctionExpressionDeclaration'] ?? false;
Expand Down Expand Up @@ -99,6 +100,20 @@ export class PhpTranspiler extends BaseTranspiler {
return `'${identifier}'`; // Transpile function reference as string
}
}
// add type support to variable
if (this.supportVariableType) {
const typeRaw = global.checker.getTypeAtLocation(node);
let type = this.getTypeFromRawType(typeRaw);
if (
ts.isPropertyDeclaration(valueDecl) ||
(ts.isParameter(valueDecl) && ts.isParameter(node.parent))
) {
if (type === 'object' && typeRaw?.intrinsicName === 'number') {
type = 'float';
}
return `${type} $${identifier}`;
}
}
}

// below is commented, due to : https://github.com/ccxt/ast-transpiler/pull/15
Expand All @@ -116,6 +131,13 @@ export class PhpTranspiler extends BaseTranspiler {
return identifier;
}

getTypeFromRawType(type) {
const typeValue = super.getTypeFromRawType(type);
if (typeValue === undefined && type?.symbol?.escapedName) {
return type.symbol.escapedName;
}
return typeValue;
}

getCustomOperatorIfAny(left, right, operator) {
const STRING_CONCAT = '.';
Expand Down
9 changes: 9 additions & 0 deletions tests/integration/source/transpilable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ class Second {

class Test {

strprop: string = "test";
numprop: number = 1;
boolprop: boolean = false;

public functionWithOptionals(a: string, c: number | undefined = undefined, d = 1) {
console.log(a);
if (c !== undefined) {
Expand Down Expand Up @@ -114,6 +118,11 @@ class Test {

this.testJavaScope();
}

someMethod(arg1: boolean, arg2: number) {
const res = arg1;
console.log(res);
}
}

export {
Expand Down
20 changes: 18 additions & 2 deletions tests/integration/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ const PY_TRANSPILABLE_FILE = "./tests/integration/py/transpilable.py";
const PHP_TRANSPILABLE_FILE = "./tests/integration/php/transpilable.php";
const CS_TRANSPILABLE_FILE = "./tests/integration/cs/transpilable.cs";
const GO_TRANSPILABLE_FILE = "./tests/integration/go/transpilable.go";

const PHP_TRANSPILABLE_FILE_WITH_TYPES = "./tests/integration/php/transpilable_with_types.php";
const JAVA_TRANSPILABLE_FILE = "./tests/integration/java/app/src/main/java/org/example/Transpilable.java";


Expand Down Expand Up @@ -37,6 +39,13 @@ const langConfig = [
language: "go",
async: true
},
{
language: "php",
async: true,
parser: {
supportVariableType: true
}
},
{
language: "java",
},
Expand All @@ -55,8 +64,12 @@ function transpileTests() {
const transpiler = new Transpiler(parseConfig);
const result = transpiler.transpileDifferentLanguagesByPath(langConfig as any, TS_TRANSPILABLE_FILE);

let phpRes = `<?php\nfunction custom_echo($x){ echo (string)$x . "\n";}\n${result[2].content}\n?>` as string;
phpRes = (phpRes as any).replaceAll('var_dump', 'custom_echo');
let phpResWrapper = (content) => {
const res = `<?php\nfunction custom_echo($x){ echo (string)$x . "\n";}\n${content}\n?>` as string;
return (res as any).replaceAll('var_dump', 'custom_echo');
};
const phpRes = phpResWrapper(result[2].content);
const phpResWithTypes = phpResWrapper(result[4].content);
const pythonAsync = result[1].content;
let csharp = 'namespace tests;\n' + result[0].content;
csharp = csharp.replace('class Test', 'partial class Test');
Expand All @@ -74,6 +87,7 @@ function transpileTests() {
const go = 'package main\n' + goImports + result[3].content;

writeFileSync(PHP_TRANSPILABLE_FILE, phpRes.toString());
writeFileSync(PHP_TRANSPILABLE_FILE_WITH_TYPES, phpResWithTypes.toString());
writeFileSync(PY_TRANSPILABLE_FILE, pythonAsync);
writeFileSync(CS_TRANSPILABLE_FILE, csharp);
writeFileSync(GO_TRANSPILABLE_FILE, go);
Expand All @@ -85,6 +99,8 @@ function runCommand(command) {
exec(command, (error, stdout, stderr) => {
if (stderr !== undefined || stderr !== null) {
stderr = stderr.replace('Debugger attached.\nWaiting for the debugger to disconnect...\n', '');
// fix for windows
stderr = stderr.replace('Debugger attached.\r','').replace('\nWaiting for the debugger to disconnect...\r\n', '');
}
if (stderr.startsWith("Debugger listening") && stderr.includes("For help, see: https://nodejs.org/en/docs/inspector")) {
stderr = undefined;
Expand Down
46 changes: 46 additions & 0 deletions tests/phpTranspiler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ jest.mock('module',()=>({
}));

let transpiler: Transpiler;
let transpilerWithType: Transpiler;


beforeAll(() => {
const config = {
Expand All @@ -18,6 +20,14 @@ beforeAll(() => {
}
}
transpiler = new Transpiler(config);


const config2 ={
'php': {
'supportVariableType': true
}
}
transpilerWithType = new Transpiler(config2);
})

describe('php transpiling tests', () => {
Expand Down Expand Up @@ -896,4 +906,40 @@ describe('php transpiling tests', () => {
const output = transpiler.transpilePhp(ts).content;
expect(output).toBe(result);
});
test('should support types', () => {
const nl = '\n';
const ts =
'class RefClass {}' + nl +
'class BasicClass {' + nl +
' public stringProp: string;' + nl +
' public numProp: number;' + nl +
' public boolProp: boolean;' + nl +
' public refProp: RefClass;' + nl +
' public constructor(arg1: string, arg2: number, arg3: boolean, arg4: RefClass) {' + nl +
' this.stringProp = arg1;' + nl +
' this.numProp = arg2;' + nl +
' this.boolProp = arg3;' + nl +
' this.refProp = arg4;' + nl +
' }' + nl +
'}';
const php =
'class RefClass {' + nl +
'' + nl +
'}' + nl +
'class BasicClass {' + nl +
' public string $stringProp;' + nl +
' public object $numProp;' + nl +
' public bool $boolProp;' + nl +
' public RefClass $refProp;' + nl +
'' + nl +
' function __construct(string $arg1, object $arg2, bool $arg3, RefClass $arg4) {' + nl +
' $this->stringProp = $arg1;' + nl +
' $this->numProp = $arg2;' + nl +
' $this->boolProp = $arg3;' + nl +
' $this->refProp = $arg4;' + nl +
' }' + nl +
'}' + nl;
const output = transpilerWithType.transpilePhp(ts).content;
expect(output).toBe(php);
});
});
Loading