├── src ├── ts2php │ ├── components │ │ ├── codegen │ │ │ ├── programUtils │ │ │ │ └── __empty.ts │ │ │ ├── defaultCompilerOptions.ts │ │ │ └── nodeFlagStore.ts │ │ ├── unusedCodeElimination │ │ │ └── usageGraph │ │ │ │ ├── index.ts │ │ │ │ └── nodeData.ts │ │ ├── typeInference │ │ │ ├── customTypehintsList.ts │ │ │ └── basicTypesMap.ts │ │ └── cli │ │ │ └── defaults.ts │ ├── utils │ │ ├── pathsAndNames │ │ │ ├── __tests__ │ │ │ │ ├── specimens │ │ │ │ │ ├── fileInRootFolder.css │ │ │ │ │ ├── fileInRootFolder.ts │ │ │ │ │ ├── module │ │ │ │ │ │ ├── fileInSameFolder.css │ │ │ │ │ │ ├── fileInSameFolder.ts │ │ │ │ │ │ ├── oneMoreModule.ts │ │ │ │ │ │ ├── otherModule │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ ├── oneMoreModule │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ └── subfolder │ │ │ │ │ │ │ ├── fileInSubfolder.css │ │ │ │ │ │ │ └── fileInSubfolder.ts │ │ │ │ │ ├── fromTsPaths_first │ │ │ │ │ │ ├── subModule │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ └── moduleInTsFirstPath.ts │ │ │ │ │ └── fromTsPaths_second │ │ │ │ │ │ └── moduleInTsSecondPath.ts │ │ │ │ └── pathsAndNames.spec.ts │ │ │ └── index.ts │ │ ├── hrtime.ts │ │ ├── sha1.ts │ │ ├── tsSupportExtensions.ts │ │ ├── hasExport.ts │ │ └── regexFlags.ts │ ├── renderers │ │ ├── numericLiteral.ts │ │ ├── regularExpressionLiteral.ts │ │ ├── jsxText.ts │ │ ├── stdlib │ │ │ ├── _propName.ts │ │ │ ├── toString.ts │ │ │ ├── parse.ts │ │ │ ├── objectKeys.ts │ │ │ ├── objectValues.ts │ │ │ ├── arrayIsArray.ts │ │ │ ├── stringTrim.ts │ │ │ └── typecastConstructors.ts │ │ ├── asExpression.ts │ │ ├── spreadElement.ts │ │ ├── caseBlock.ts │ │ ├── spreadAssignment.ts │ │ ├── block.ts │ │ ├── jsxFragment.ts │ │ ├── variableDeclarationList.ts │ │ ├── parenthesizedExpression.ts │ │ ├── defaultClause.ts │ │ ├── caseClause.ts │ │ ├── elementAccessExpression.ts │ │ ├── typeAssertionExpression.ts │ │ ├── whileStatement.ts │ │ ├── doWhileStatement.ts │ │ ├── ternaryOperator.ts │ │ ├── computedPropertyName.ts │ │ ├── exports.ts │ │ ├── switchStatement.ts │ │ ├── newExpression.ts │ │ ├── expressionStatement.ts │ │ ├── stringLiteral.ts │ │ ├── this.ts │ │ ├── templateString.ts │ │ ├── typeofExpression.ts │ │ ├── arrayLiteralExpression.ts │ │ └── variableStatement.ts │ └── internalConfig │ │ ├── intrinsicElements.ts │ │ ├── phpPrettierOptions.ts │ │ └── jsBuiltins.ts ├── __tests__ │ ├── specimens │ │ ├── astTools │ │ │ ├── flagParentOfType.ts │ │ │ ├── synList.ts │ │ │ ├── leftExpr.ts │ │ │ └── identities.ts │ │ ├── byType │ │ │ ├── identifier.ts │ │ │ ├── binaryExpression.ts │ │ │ ├── callExpression.ts │ │ │ ├── elementAccessExpression.ts │ │ │ ├── objectLiteralExpression.ts │ │ │ ├── enumImport.ts │ │ │ ├── export.ts │ │ │ ├── block.ts │ │ │ ├── functionDeclaration.ts │ │ │ ├── objectComputedProperties.ts │ │ │ ├── classesUseInExternal.tsx │ │ │ ├── serverIfStatement.ts │ │ │ ├── arrowFunction.ts │ │ │ ├── tsInternals.ts │ │ │ ├── import.ts │ │ │ ├── ifStatement.ts │ │ │ ├── propertyAccessExpression.ts │ │ │ ├── templateString.ts │ │ │ ├── jsx.tsx │ │ │ ├── JsxModule.php │ │ │ ├── restOperator.ts │ │ │ ├── enum.ts │ │ │ ├── enum │ │ │ │ ├── NumberedEnum.php │ │ │ │ ├── ForcedNumberedEnum.php │ │ │ │ └── ValuesBasedEnum.php │ │ │ ├── loops.ts │ │ │ ├── spreadOperator.ts │ │ │ ├── classes.tsx │ │ │ ├── IdentifierModule.php │ │ │ ├── BinaryExpressionModule.php │ │ │ ├── parameterDestructuring.ts │ │ │ ├── EnumImportModule.php │ │ │ ├── CallExpressionModule.php │ │ │ ├── BlockModule.php │ │ │ ├── ElementAccessExpressionModule.php │ │ │ ├── ExportModule.php │ │ │ ├── FunctionDeclarationModule.php │ │ │ ├── ObjectLiteralExpressionModule.php │ │ │ ├── switchStatement.ts │ │ │ ├── ImportModule.php │ │ │ ├── ClassesUseInExternalModule.php │ │ │ ├── ServerIfStatementModule.php │ │ │ └── ArrowFunctionModule.php │ │ ├── components │ │ │ ├── ImportResolve.css │ │ │ ├── PathResolve │ │ │ │ ├── tsx.tsx │ │ │ │ ├── helpers.ts │ │ │ │ ├── index.ts │ │ │ │ ├── TsxModule.php │ │ │ │ └── PathResolveModule.php │ │ │ ├── static │ │ │ │ ├── KeywordTestModule.ts │ │ │ │ ├── KeywordTestModuleImport.ts │ │ │ │ ├── KeywordTestComponentImport.tsx │ │ │ │ └── KeywordTestComponent.tsx │ │ │ ├── CyclicDeps │ │ │ │ ├── entry1.ts │ │ │ │ ├── entry2.ts │ │ │ │ ├── index.ts │ │ │ │ ├── Entry1Module.php │ │ │ │ ├── CyclicDepsModule.php │ │ │ │ └── Entry2Module.php │ │ │ ├── CyclicDepsEntry.ts │ │ │ ├── PathResolveReact │ │ │ │ ├── Component.tsx │ │ │ │ ├── index.tsx │ │ │ │ ├── ComponentModule.php │ │ │ │ ├── PathResolveReactModule.php │ │ │ │ ├── index │ │ │ │ │ ├── ReactPathResolveModule.php │ │ │ │ │ └── ReactPathResolveAnonymousModule.php │ │ │ │ └── Component │ │ │ │ │ ├── PathResolveReactComponent2.php │ │ │ │ │ ├── ReactPathResolveComponent.php │ │ │ │ │ └── ReactPathResolveAnonymousComponent.php │ │ │ ├── TypedComponent.tsx │ │ │ ├── InnerHtmlComponent.tsx │ │ │ ├── NullReturnInComponent.tsx │ │ │ ├── DummyComponent.tsx │ │ │ ├── ImportResolve2.tsx │ │ │ ├── NestedConditionalComponent.tsx │ │ │ ├── ComponentWithOuterFunction.tsx │ │ │ ├── NestedComponent.tsx │ │ │ ├── BasicComponent.tsx │ │ │ ├── ComponentBinaryOperators.tsx │ │ │ ├── ImportResolve.tsx │ │ │ ├── BasicComponentWithProps.tsx │ │ │ ├── NestedComponentWithDummyInProps.tsx │ │ │ ├── elephize_static │ │ │ │ ├── KeywordTestModuleModule.php │ │ │ │ ├── KeywordTestModuleImportModule.php │ │ │ │ └── KeywordTestComponentImportModule.php │ │ │ ├── InheritedProps.tsx │ │ │ ├── CyclicDepsEntryModule.php │ │ │ ├── TwoComponentsModule.php │ │ │ ├── NestedConditionalComponent │ │ │ │ └── ComponentWrapper.php │ │ │ ├── ReexportResolve.tsx │ │ │ ├── InheritedPropsModule.php │ │ │ ├── ComponentWithOuterFunctionModule.php │ │ │ └── TypedComponent │ │ │ │ └── TypedComponent.php │ │ ├── stringMethods │ │ │ ├── trim.ts │ │ │ ├── join.ts │ │ │ ├── substr.ts │ │ │ ├── match.ts │ │ │ ├── strIncludes.ts │ │ │ ├── startsWith.ts │ │ │ ├── parse.ts │ │ │ ├── strSlice.ts │ │ │ ├── strIndexOf.ts │ │ │ ├── split.ts │ │ │ ├── replace.ts │ │ │ ├── TrimModule.php │ │ │ ├── JoinModule.php │ │ │ ├── ParseModule.php │ │ │ ├── SubstrModule.php │ │ │ ├── MatchModule.php │ │ │ ├── StartsWithModule.php │ │ │ └── StrIncludesModule.php │ │ ├── misc │ │ │ ├── astHooks.tsx │ │ │ ├── toReplace.ts │ │ │ ├── constructorTypeCast.ts │ │ │ ├── __toIgnore.ts │ │ │ ├── exportedTypes.ts │ │ │ ├── __toIgnoreFolder │ │ │ │ └── toIgnore.ts │ │ │ ├── complexObjectFuncs.ts │ │ │ ├── defaultOperator.ts │ │ │ ├── importedTypes.tsx │ │ │ ├── typecast.tsx │ │ │ ├── elephizeIgnore.tsx │ │ │ ├── defaultValues.ts │ │ │ ├── typeUnion.ts │ │ │ ├── toReplaceIndex │ │ │ │ └── index.ts │ │ │ ├── ElephizeIgnoreModule.php │ │ │ ├── ExportedTypesModule.php │ │ │ ├── AstHooksModule.php │ │ │ ├── AllowedOnClickModule.php │ │ │ ├── allowedOnClick.tsx │ │ │ ├── customIsomorphics.tsx │ │ │ ├── elephizeIgnore │ │ │ │ └── ComponentIgnore.php │ │ │ ├── unusedVarsElimination.ts │ │ │ ├── allowedOnClick │ │ │ │ ├── AllowedGlobalClickVar3.php │ │ │ │ ├── AllowedGlobalClick.php │ │ │ │ └── AllowedGlobalClickVar.php │ │ │ └── ComplexObjectFuncsModule.php │ │ ├── arrayMethods │ │ │ ├── pushPop.ts │ │ │ ├── includes.ts │ │ │ ├── find.ts │ │ │ ├── filter.ts │ │ │ ├── some.ts │ │ │ ├── isArray.ts │ │ │ ├── slice.ts │ │ │ ├── indexOf.ts │ │ │ ├── splice.ts │ │ │ ├── reduce.ts │ │ │ ├── forEach.ts │ │ │ ├── map.ts │ │ │ ├── PushPopModule.php │ │ │ └── IncludesModule.php │ │ ├── objectMethods │ │ │ ├── keys.ts │ │ │ ├── values.ts │ │ │ ├── hasOwnProperty.ts │ │ │ ├── KeysModule.php │ │ │ ├── ValuesModule.php │ │ │ └── HasOwnPropertyModule.php │ │ ├── mathMethods │ │ │ ├── constants.ts │ │ │ ├── trigonometry.ts │ │ │ ├── basic.ts │ │ │ └── ConstantsModule.php │ │ ├── functionMethods │ │ │ └── closures.ts │ │ ├── ToReplace.php │ │ └── fixes │ │ │ ├── ExcessiveEscaping.tsx │ │ │ └── EscapeHtmlCharsModule.php │ ├── watchSpecimens │ │ ├── importNewFileToImport.ts │ │ ├── importNewFile.entry.ts │ │ ├── unusedVariable.entry.ts │ │ ├── brokenTsSyntax.entry.ts │ │ ├── brokenTsTyping.entry.ts │ │ ├── modifyFileTypehints.entry.ts │ │ ├── undefVariableUsage.entry.ts │ │ ├── variableUsage.entry.ts │ │ ├── removeImport.entry.ts │ │ ├── modifyFile.entry.ts │ │ ├── modifyFile.1.patch │ │ ├── modifyFile.2.patch │ │ ├── unusedVariable.1.patch │ │ ├── unusedVariable.2.patch │ │ ├── brokenTsSyntax.1.patch │ │ ├── brokenTsSyntax.2.patch │ │ ├── brokenTsTyping.2.patch │ │ ├── removeImport.1.patch │ │ ├── importNewFile.1.patch │ │ ├── undefVariableUsage.1.patch │ │ ├── undefVariableUsage.2.patch │ │ ├── brokenTsTyping.1.patch │ │ ├── variableUsage.1.patch │ │ ├── variableUsage.2.patch │ │ ├── modifyFileTypehints.1.patch │ │ ├── modifyFileTypehints.2.patch │ │ ├── elephizeAnnotationTarget.1.patch │ │ ├── elephizeAnnotationTarget.2.patch │ │ ├── elephizeAnnotationTarget.entry.tsx │ │ ├── ImportNewFileToImportModule.1.php │ │ ├── RemoveImportModule.1.php │ │ ├── ModifyFileModule.1.php │ │ ├── BrokenTsSyntaxModule.1.php │ │ ├── VariableUsageModule.2.php │ │ ├── BrokenTsSyntaxModule.2.php │ │ ├── BrokenTsTypingModule.1.php │ │ ├── BrokenTsTypingModule.2.php │ │ ├── UnusedVariableModule.1.php │ │ ├── UndefVariableUsageModule.2.php │ │ ├── VariableUsageModule.1.php │ │ ├── UndefVariableUsageModule.1.php │ │ ├── ModifyFileTypehintsModule.1.php │ │ ├── ModifyFileTypehintsModule.2.php │ │ ├── ImportNewFileModule.1.php │ │ └── ModifyFileModule.2.php │ └── 8_customMisc.spec.ts ├── builtins │ ├── CJSModule.php │ ├── ILogFacility.php │ ├── Functional.php │ ├── StdOutLogFacility.php │ ├── Console.php │ └── RenderableComponent.php └── vendor │ ├── php-common │ └── README │ └── php-parser │ ├── README │ ├── tsconfig.json │ ├── parser │ ├── ModifierFlags.ts │ └── DocumentationMode.ts │ ├── language │ └── syntax │ │ └── ISyntaxTreeTraversable.ts │ └── text │ └── Encoding.ts ├── bin └── elephize ├── demo ├── public │ ├── favicon.ico │ ├── logo192.png │ ├── logo512.png │ ├── robots.txt │ ├── index.php │ ├── autoload.php │ └── manifest.json ├── src │ ├── index.tsx │ ├── index.css │ ├── render.tsx │ ├── layout.html │ ├── utils │ │ └── classnames.ts │ ├── server.tsx │ ├── App.css │ └── components │ │ └── iso_palette │ │ └── Radio │ │ └── Radio.iso.tsx ├── .elephizerc └── tsconfig.json ├── doc └── project-description │ ├── child-scope.png │ ├── example-for.png │ ├── func-example.png │ ├── graph-initial.png │ ├── graph-result.png │ ├── closure-example.png │ ├── example-for-ast.png │ ├── graph-reachable.png │ ├── graph-traverse.png │ ├── literal-handlers.png │ ├── example-operators.png │ ├── nested-func-example.png │ ├── example-type-inference.png │ └── about-images-edit.txt ├── external ├── prepack.sh ├── postpack.sh ├── htmlformat ├── check_kphp.sh ├── check_specimens.sh └── run_compiled_kphp.sh ├── .gitattributes ├── .eslintignore ├── tsfmt.json ├── jest.config.js ├── .gitignore ├── .npmignore ├── elephizerc.example.json ├── .github ├── CODEOWNERS └── workflows │ └── checks.yml ├── types └── global │ └── index.d.ts └── tsconfig.json /src/ts2php/components/codegen/programUtils/__empty.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /bin/elephize: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | require('../dist/src/cli.js'); 3 | -------------------------------------------------------------------------------- /src/ts2php/utils/pathsAndNames/__tests__/specimens/fileInRootFolder.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/ts2php/utils/pathsAndNames/__tests__/specimens/fileInRootFolder.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/__tests__/specimens/astTools/flagParentOfType.ts: -------------------------------------------------------------------------------- 1 | var h = require('react'); -------------------------------------------------------------------------------- /src/ts2php/utils/pathsAndNames/__tests__/specimens/module/fileInSameFolder.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/ts2php/utils/pathsAndNames/__tests__/specimens/module/fileInSameFolder.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/ts2php/utils/pathsAndNames/__tests__/specimens/module/oneMoreModule.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/ts2php/utils/pathsAndNames/__tests__/specimens/module/otherModule/index.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/ts2php/utils/pathsAndNames/__tests__/specimens/module/oneMoreModule/index.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/__tests__/specimens/astTools/synList.ts: -------------------------------------------------------------------------------- 1 | const tsl = [1, '2', (1 + 2), false]; 2 | -------------------------------------------------------------------------------- /src/__tests__/specimens/byType/identifier.ts: -------------------------------------------------------------------------------- 1 | let a1 = 1; 2 | console.log(a1, a1); 3 | -------------------------------------------------------------------------------- /src/ts2php/utils/pathsAndNames/__tests__/specimens/fromTsPaths_first/subModule/index.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/ts2php/utils/pathsAndNames/__tests__/specimens/module/subfolder/fileInSubfolder.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/ts2php/utils/pathsAndNames/__tests__/specimens/module/subfolder/fileInSubfolder.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/__tests__/specimens/byType/binaryExpression.ts: -------------------------------------------------------------------------------- 1 | let a = 2 + 5; 2 | console.log(a); 3 | -------------------------------------------------------------------------------- /src/ts2php/utils/pathsAndNames/__tests__/specimens/fromTsPaths_first/moduleInTsFirstPath.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/ts2php/utils/pathsAndNames/__tests__/specimens/fromTsPaths_second/moduleInTsSecondPath.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VKCOM/elephize/HEAD/demo/public/favicon.ico -------------------------------------------------------------------------------- /demo/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VKCOM/elephize/HEAD/demo/public/logo192.png -------------------------------------------------------------------------------- /demo/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VKCOM/elephize/HEAD/demo/public/logo512.png -------------------------------------------------------------------------------- /demo/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /src/__tests__/specimens/astTools/leftExpr.ts: -------------------------------------------------------------------------------- 1 | const ident: any = []; 2 | ident.prop.prop2.access = 1; -------------------------------------------------------------------------------- /src/__tests__/specimens/components/ImportResolve.css: -------------------------------------------------------------------------------- 1 | .ClassName { 2 | border: 1px solid red; 3 | } -------------------------------------------------------------------------------- /src/__tests__/watchSpecimens/importNewFileToImport.ts: -------------------------------------------------------------------------------- 1 | export const inftest1 = (b: number) => b + 1; 2 | -------------------------------------------------------------------------------- /src/ts2php/components/unusedCodeElimination/usageGraph/index.ts: -------------------------------------------------------------------------------- 1 | export { Scope } from './scope'; 2 | -------------------------------------------------------------------------------- /src/__tests__/specimens/byType/callExpression.ts: -------------------------------------------------------------------------------- 1 | let c = document.getElementById('test'); 2 | console.log(c); 3 | -------------------------------------------------------------------------------- /src/__tests__/specimens/components/PathResolve/tsx.tsx: -------------------------------------------------------------------------------- 1 | export function getTest() { 2 | return 'test'; 3 | } -------------------------------------------------------------------------------- /src/builtins/CJSModule.php: -------------------------------------------------------------------------------- 1 | { 2 | return x + 4.; 3 | }; 4 | -------------------------------------------------------------------------------- /doc/project-description/child-scope.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VKCOM/elephize/HEAD/doc/project-description/child-scope.png -------------------------------------------------------------------------------- /doc/project-description/example-for.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VKCOM/elephize/HEAD/doc/project-description/example-for.png -------------------------------------------------------------------------------- /src/__tests__/specimens/byType/objectLiteralExpression.ts: -------------------------------------------------------------------------------- 1 | let e = { a: 123, b: 321, c: 222, d: '123' }; 2 | console.log(e); 3 | -------------------------------------------------------------------------------- /src/__tests__/watchSpecimens/importNewFile.entry.ts: -------------------------------------------------------------------------------- 1 | const inftest2 = (b: number) => b + 1; 2 | console.log(inftest2(1)); 3 | -------------------------------------------------------------------------------- /doc/project-description/func-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VKCOM/elephize/HEAD/doc/project-description/func-example.png -------------------------------------------------------------------------------- /doc/project-description/graph-initial.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VKCOM/elephize/HEAD/doc/project-description/graph-initial.png -------------------------------------------------------------------------------- /doc/project-description/graph-result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VKCOM/elephize/HEAD/doc/project-description/graph-result.png -------------------------------------------------------------------------------- /doc/project-description/closure-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VKCOM/elephize/HEAD/doc/project-description/closure-example.png -------------------------------------------------------------------------------- /doc/project-description/example-for-ast.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VKCOM/elephize/HEAD/doc/project-description/example-for-ast.png -------------------------------------------------------------------------------- /doc/project-description/graph-reachable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VKCOM/elephize/HEAD/doc/project-description/graph-reachable.png -------------------------------------------------------------------------------- /doc/project-description/graph-traverse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VKCOM/elephize/HEAD/doc/project-description/graph-traverse.png -------------------------------------------------------------------------------- /doc/project-description/literal-handlers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VKCOM/elephize/HEAD/doc/project-description/literal-handlers.png -------------------------------------------------------------------------------- /external/prepack.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | sed -i.bak -e 's/"main": "src\/cli.ts"/"main": "dist\/src\/cli.js"/g' package.json 4 | -------------------------------------------------------------------------------- /doc/project-description/example-operators.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VKCOM/elephize/HEAD/doc/project-description/example-operators.png -------------------------------------------------------------------------------- /src/__tests__/specimens/components/static/KeywordTestModule.ts: -------------------------------------------------------------------------------- 1 | export function KeywordTestModule() { 2 | return 'test' + Date.now(); 3 | } -------------------------------------------------------------------------------- /doc/project-description/nested-func-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VKCOM/elephize/HEAD/doc/project-description/nested-func-example.png -------------------------------------------------------------------------------- /doc/project-description/example-type-inference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VKCOM/elephize/HEAD/doc/project-description/example-type-inference.png -------------------------------------------------------------------------------- /src/ts2php/utils/hrtime.ts: -------------------------------------------------------------------------------- 1 | export const getTimeMarker = () => { 2 | const hrt = process.hrtime(); 3 | return `${hrt[0] * 1e9 + hrt[1]}`; 4 | }; 5 | -------------------------------------------------------------------------------- /src/ts2php/renderers/numericLiteral.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | export const tNumericLiteral = (node: ts.NumericLiteral) => node.getText(); 3 | -------------------------------------------------------------------------------- /src/__tests__/specimens/arrayMethods/pushPop.ts: -------------------------------------------------------------------------------- 1 | let aSt = [1, 2, 3]; 2 | aSt.push(3); 3 | aSt.push(1, 2, 3); 4 | let bSt = aSt.pop(); 5 | console.log(bSt); 6 | -------------------------------------------------------------------------------- /src/__tests__/specimens/byType/enumImport.ts: -------------------------------------------------------------------------------- 1 | import { ValuesBasedEnum } from './enum'; 2 | 3 | const enut4 = ValuesBasedEnum.lol; 4 | 5 | console.log(enut4); 6 | -------------------------------------------------------------------------------- /src/__tests__/specimens/misc/toReplace.ts: -------------------------------------------------------------------------------- 1 | export const getLang = (test: string) => '' + test; 2 | export const getLangStatic = (test: string) => '' + test; 3 | -------------------------------------------------------------------------------- /src/__tests__/specimens/stringMethods/join.ts: -------------------------------------------------------------------------------- 1 | let ajn = ['12', '3', '45']; 2 | let bjn = ajn.join(':'); 3 | let cjn = ajn.join(); 4 | console.log(bjn, cjn); 5 | -------------------------------------------------------------------------------- /src/__tests__/watchSpecimens/unusedVariable.entry.ts: -------------------------------------------------------------------------------- 1 | const uvtest2 = (b: number) => { 2 | let a = 1 + b; 3 | return a; 4 | }; 5 | console.log(uvtest2(1)); 6 | -------------------------------------------------------------------------------- /src/__tests__/specimens/arrayMethods/includes.ts: -------------------------------------------------------------------------------- 1 | let ain = [1, 2, 3]; 2 | let bin = ain.includes(2); 3 | let cin = ain.includes(2, 1); 4 | console.log(bin, cin); 5 | -------------------------------------------------------------------------------- /src/__tests__/specimens/byType/export.ts: -------------------------------------------------------------------------------- 1 | export function test() { 2 | return 1 + 2; 3 | } 4 | export const test2 = 1; 5 | 6 | export const test333 = () => 'kek'; 7 | -------------------------------------------------------------------------------- /src/__tests__/specimens/stringMethods/substr.ts: -------------------------------------------------------------------------------- 1 | let asub = '12345'; 2 | let bsub = asub.substr(2); 3 | let csub = asub.substr(2, 2); 4 | console.log(bsub, csub); 5 | -------------------------------------------------------------------------------- /src/__tests__/watchSpecimens/brokenTsSyntax.entry.ts: -------------------------------------------------------------------------------- 1 | const btstest2 = (b: number) => { 2 | let a = 1 + b; 3 | return a; 4 | }; 5 | console.log(btstest2(1)); 6 | -------------------------------------------------------------------------------- /src/__tests__/watchSpecimens/brokenTsTyping.entry.ts: -------------------------------------------------------------------------------- 1 | const btttest2 = (b: number) => { 2 | let a = 1 + b; 3 | return a; 4 | }; 5 | console.log(btttest2(1)); 6 | -------------------------------------------------------------------------------- /src/__tests__/watchSpecimens/modifyFileTypehints.entry.ts: -------------------------------------------------------------------------------- 1 | const mfttest = (a: number, b: number) => a.toString() + b.toString(); 2 | console.log(mfttest(1, 3)); 3 | -------------------------------------------------------------------------------- /src/__tests__/watchSpecimens/undefVariableUsage.entry.ts: -------------------------------------------------------------------------------- 1 | const uvutest2 = (b: number) => { 2 | let a = 1 + b; 3 | return a; 4 | }; 5 | console.log(uvutest2(1)); 6 | -------------------------------------------------------------------------------- /src/__tests__/specimens/byType/block.ts: -------------------------------------------------------------------------------- 1 | let t1 = 1; 2 | let t = function() { 3 | let aa = t1 + 2; 4 | let bb = aa + 1; 5 | return bb; 6 | }; 7 | console.log(t()); 8 | -------------------------------------------------------------------------------- /src/__tests__/specimens/components/CyclicDeps/entry1.ts: -------------------------------------------------------------------------------- 1 | import { getCyclicEntry2 } from "./entry2"; 2 | 3 | export function getCyclicEntry1() { 4 | getCyclicEntry2(); 5 | } -------------------------------------------------------------------------------- /src/__tests__/specimens/objectMethods/keys.ts: -------------------------------------------------------------------------------- 1 | let ok = { 2 | 1: 123, 3 | 'test': 321, 4 | 23: 1111 5 | }; 6 | let okkeys = Object.keys(ok); 7 | console.log(okkeys); 8 | -------------------------------------------------------------------------------- /src/__tests__/specimens/objectMethods/values.ts: -------------------------------------------------------------------------------- 1 | let ok = { 2 | 1: 123, 3 | 'test': 321, 4 | 23: 1111 5 | }; 6 | let okvals = Object.values(ok); 7 | console.log(okvals); 8 | -------------------------------------------------------------------------------- /src/__tests__/specimens/stringMethods/match.ts: -------------------------------------------------------------------------------- 1 | let amat = '12:3:45'; 2 | let dmat = amat.match(/[24]/i); 3 | let emat = amat.match(/[24]/ig); 4 | console.log(dmat, emat); 5 | -------------------------------------------------------------------------------- /src/__tests__/specimens/stringMethods/strIncludes.ts: -------------------------------------------------------------------------------- 1 | let ainc = '12345'; 2 | let binc = ainc.includes('2'); 3 | let cinc = ainc.includes('2', 2); 4 | console.log(binc, cinc); 5 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | dist/**/* linguist-generated 2 | src/vendor/**/* linguist-generated 3 | data/domattrs.json linguist-generated 4 | data/intrinsicElements.json linguist-generated 5 | -------------------------------------------------------------------------------- /src/__tests__/specimens/byType/functionDeclaration.ts: -------------------------------------------------------------------------------- 1 | function test1(b: number) { 2 | let a = 1 + b; 3 | this.test111 = 1; 4 | return a; 5 | } 6 | console.log(test1(1)); 7 | -------------------------------------------------------------------------------- /src/__tests__/specimens/misc/constructorTypeCast.ts: -------------------------------------------------------------------------------- 1 | const tca = '1'; 2 | const tcb = Number(tca); 3 | const tcc = String(tcb); 4 | const tcd = Boolean(tcc); 5 | console.log(tcd); 6 | -------------------------------------------------------------------------------- /src/__tests__/specimens/stringMethods/startsWith.ts: -------------------------------------------------------------------------------- 1 | let astw = '12345'; 2 | let bstw = astw.startsWith('1'); 3 | let cstw = astw.startsWith('2', 1); 4 | console.log(bstw, cstw); 5 | -------------------------------------------------------------------------------- /src/__tests__/watchSpecimens/variableUsage.entry.ts: -------------------------------------------------------------------------------- 1 | const vutest2 = (b: number) => { 2 | let a = 1 + b; 3 | const c = b; 4 | return a; 5 | }; 6 | console.log(vutest2(1)); 7 | -------------------------------------------------------------------------------- /src/__tests__/specimens/arrayMethods/find.ts: -------------------------------------------------------------------------------- 1 | let aff = [1, 2, 3]; 2 | let bff = aff.find((el) => el % 2); 3 | let cff = aff.find((el, idx) => el * idx % 2); 4 | console.log(bff, cff); 5 | -------------------------------------------------------------------------------- /src/__tests__/specimens/byType/objectComputedProperties.ts: -------------------------------------------------------------------------------- 1 | let ocpProp = 'sdf'; 2 | let ocp1 = { a: 123, [`${ocpProp}__asd`]: 321, [ocpProp]: 222, d: '123' }; 3 | console.log(ocp1); 4 | -------------------------------------------------------------------------------- /src/__tests__/specimens/arrayMethods/filter.ts: -------------------------------------------------------------------------------- 1 | let afl = [1, 2, 3]; 2 | let bfl = afl.filter((el) => el % 2); 3 | let cfl = afl.filter((el, idx) => el * idx % 2); 4 | console.log(bfl, cfl); 5 | -------------------------------------------------------------------------------- /src/__tests__/specimens/arrayMethods/some.ts: -------------------------------------------------------------------------------- 1 | let asm = [1, 2, 3]; 2 | let bsm = asm.some((val) => val > 1); 3 | let csm = asm.some((val, idx) => val * idx % 2); 4 | console.log(bsm, csm); 5 | -------------------------------------------------------------------------------- /src/__tests__/specimens/stringMethods/parse.ts: -------------------------------------------------------------------------------- 1 | const pria = parseInt('123'); 2 | const prib = parseInt('231', 16); 3 | const prfa = parseFloat('123.23'); 4 | console.log(pria, prib, prfa); 5 | -------------------------------------------------------------------------------- /src/__tests__/specimens/byType/classesUseInExternal.tsx: -------------------------------------------------------------------------------- 1 | import { Classes } from './classes'; 2 | 3 | const c = new Classes(); 4 | c.setter1(123); 5 | console.log(c.getter1() + Classes.MY_VAR); 6 | -------------------------------------------------------------------------------- /src/__tests__/specimens/byType/serverIfStatement.ts: -------------------------------------------------------------------------------- 1 | let sisAa = 'test1'; 2 | let sisAb = 'test2'; 3 | let sisAc = window._elephizeIsServer ? sisAa : sisAb; 4 | console.log(sisAb, sisAa, sisAc); 5 | -------------------------------------------------------------------------------- /src/__tests__/specimens/objectMethods/hasOwnProperty.ts: -------------------------------------------------------------------------------- 1 | let hopobj = { 2 | 1: 123, 3 | 'test': 321, 4 | 23: 1111 5 | }; 6 | let hop = hopobj.hasOwnProperty('test'); 7 | console.log(hop); 8 | -------------------------------------------------------------------------------- /src/__tests__/specimens/misc/__toIgnore.ts: -------------------------------------------------------------------------------- 1 | // @ts-ignore 2 | export const getSome = () => typeof '123' as string; // typename typo is intentional to make sure this file is not parsed at all! 3 | -------------------------------------------------------------------------------- /src/__tests__/specimens/misc/exportedTypes.ts: -------------------------------------------------------------------------------- 1 | export interface TestInterface { 2 | lol: number; 3 | } 4 | 5 | export type TestType = number | TestInterface; 6 | 7 | export const justConst = 1; 8 | -------------------------------------------------------------------------------- /src/__tests__/specimens/stringMethods/strSlice.ts: -------------------------------------------------------------------------------- 1 | let asls = '12345'; 2 | let a1sls = asls.slice(); 3 | let bsls = asls.slice(2); 4 | let csls = asls.slice(2, 4); 5 | console.log(a1sls, bsls, csls); 6 | -------------------------------------------------------------------------------- /src/__tests__/specimens/arrayMethods/isArray.ts: -------------------------------------------------------------------------------- 1 | const iaa = Array.isArray([1, 2, 3, 4]); 2 | const iab = Array.isArray({ a: 1, b: 2 }); 3 | const iac = Array.isArray(123); 4 | console.log(iaa, iab, iac); 5 | -------------------------------------------------------------------------------- /src/__tests__/specimens/arrayMethods/slice.ts: -------------------------------------------------------------------------------- 1 | let aSl = [1, 2, 3, 4, 5, 6, 7, 8, 9]; 2 | let bSl = aSl.slice(); 3 | let cSl = aSl.slice(1); 4 | let dSl = aSl.slice(1, 2); 5 | console.log(bSl, cSl, dSl); 6 | -------------------------------------------------------------------------------- /src/__tests__/specimens/components/CyclicDepsEntry.ts: -------------------------------------------------------------------------------- 1 | import { getCyclicEntry1, getCyclicEntry2, getCyclicEntry3 } from "./CyclicDeps"; 2 | 3 | getCyclicEntry1(); 4 | getCyclicEntry2(); 5 | getCyclicEntry3(); -------------------------------------------------------------------------------- /demo/src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import { App } from './components/App.isoentry'; 4 | 5 | ReactDOM.hydrate(, document.getElementById('root')); 6 | -------------------------------------------------------------------------------- /external/postpack.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | mv package.json.bak package.json 4 | 5 | # shellcheck disable=SC2154 6 | git tag "$npm_package_version" 7 | git push origin refs/tags/"$npm_package_version" 8 | -------------------------------------------------------------------------------- /src/__tests__/watchSpecimens/removeImport.entry.ts: -------------------------------------------------------------------------------- 1 | import { inftest1 } from './importNewFileToImport'; 2 | const inftest3 = (b: number) => b + 1; 3 | console.log(inftest3(1)); 4 | console.log(inftest1(1)); 5 | -------------------------------------------------------------------------------- /src/builtins/ILogFacility.php: -------------------------------------------------------------------------------- 1 | typeof '123' as string; // typename typo is intentional to make sure this file is not parsed at all! 3 | -------------------------------------------------------------------------------- /src/ts2php/renderers/regularExpressionLiteral.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | export const tRegularExpressionLiteral = (node: ts.RegularExpressionLiteral) => `"${node.getText().replace(/"/g, '\\"')}"`; 3 | -------------------------------------------------------------------------------- /src/__tests__/specimens/stringMethods/strIndexOf.ts: -------------------------------------------------------------------------------- 1 | let aios = '12345'; 2 | let bios = aios.indexOf('2'); 3 | let cios = aios.indexOf('2', 2); 4 | let dios = aios.lastIndexOf('2', 2); 5 | console.log(bios, cios, dios); 6 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .idea/ 3 | vendor/ 4 | dist/ 5 | demo/ 6 | 7 | src/__tests__/watchSpecimens.~/ 8 | src/__tests__/watchSpecimens___/ 9 | src/__tests__/watchSpecimens/ 10 | src/__tests__/specimens/ 11 | -------------------------------------------------------------------------------- /src/__tests__/specimens/byType/arrowFunction.ts: -------------------------------------------------------------------------------- 1 | const test2 = (b: number) => { 2 | let a = 1 + b; 3 | return a; 4 | }; 5 | const test3 = (a: number, b: number) => a + b; 6 | console.log(test2(1)); 7 | console.log(test3(1, 2)); 8 | -------------------------------------------------------------------------------- /src/__tests__/specimens/components/static/KeywordTestComponentImport.tsx: -------------------------------------------------------------------------------- 1 | import { KeywordTestComponent } from "./KeywordTestComponent"; 2 | 3 | export function KeywordTestComponentImport() { 4 | return 5 | } -------------------------------------------------------------------------------- /src/ts2php/renderers/jsxText.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { escapeTextLiteral } from '../utils/escapeString'; 3 | 4 | export function tJsxText(node: ts.JsxText) { 5 | return escapeTextLiteral(node.text); 6 | } 7 | -------------------------------------------------------------------------------- /src/__tests__/specimens/arrayMethods/indexOf.ts: -------------------------------------------------------------------------------- 1 | let aio = [1, 2, 3]; 2 | let bio = aio.indexOf(2); 3 | let cio = aio.indexOf(1, 1); 4 | let dio = aio.lastIndexOf(1); 5 | let fio = aio.lastIndexOf(1, 1); 6 | console.log(bio, cio, dio, fio); 7 | -------------------------------------------------------------------------------- /src/__tests__/watchSpecimens/modifyFile.entry.ts: -------------------------------------------------------------------------------- 1 | const mftest2 = (b: number) => { 2 | let a = 1 + b; 3 | return a; 4 | }; 5 | const mftest3 = (a: number, b: number) => a + b; 6 | console.log(mftest2(1)); 7 | console.log(mftest3(1, 2)); 8 | -------------------------------------------------------------------------------- /src/vendor/php-common/README: -------------------------------------------------------------------------------- 1 | Copied from https://github.com/mattacosta/php-common 2 | All rights belong to respective authors. 3 | 4 | Q: Why not npm package? 5 | A: Common lib is not intended to be used separately or used as package. 6 | -------------------------------------------------------------------------------- /src/__tests__/specimens/misc/complexObjectFuncs.ts: -------------------------------------------------------------------------------- 1 | export function cls(names: { [key: string]: boolean }) { 2 | // проверить, может проблема из-за чейнинга?йфй 3 | return Object.keys(names).filter((name) => names[name]).join(' '); 4 | } 5 | -------------------------------------------------------------------------------- /src/__tests__/specimens/misc/defaultOperator.ts: -------------------------------------------------------------------------------- 1 | const doa = [1, 2, 3]; 2 | const dob = false; 3 | const doc = dob || { a: true }; 4 | const dod = doa[5] || doa.length; 5 | const doe = dob || doa[5] || doc || dod; 6 | console.log(doc, dod, doe); 7 | -------------------------------------------------------------------------------- /src/__tests__/specimens/components/CyclicDeps/entry2.ts: -------------------------------------------------------------------------------- 1 | import { getCyclicEntry1 } from "./entry1"; 2 | 3 | export function getCyclicEntry2() { 4 | return '1'; 5 | } 6 | 7 | export function getCyclicEntry3() { 8 | return getCyclicEntry1(); 9 | } -------------------------------------------------------------------------------- /src/__tests__/specimens/stringMethods/split.ts: -------------------------------------------------------------------------------- 1 | let aspl = '12:3:45'; 2 | let bspl = aspl.split(':'); 3 | let cspl = aspl.split(':', 1); 4 | let dspl = aspl.split(/[24]/i); 5 | let espl = aspl.split(/[24]/i, 1); 6 | console.log(bspl, cspl, dspl, espl); 7 | -------------------------------------------------------------------------------- /src/__tests__/specimens/arrayMethods/splice.ts: -------------------------------------------------------------------------------- 1 | let aSpl = [1, 2, 3]; 2 | let bSpl = aSpl.splice(1); 3 | let cSpl = aSpl.splice(1, 1); 4 | let dSpl = aSpl.splice(1, 0, 4, 5); 5 | let eSpl = aSpl.splice(1, 2, 4, 5, 6, 7); 6 | console.log(bSpl, cSpl, dSpl, eSpl); 7 | -------------------------------------------------------------------------------- /src/__tests__/specimens/arrayMethods/reduce.ts: -------------------------------------------------------------------------------- 1 | let ar = [1, 2, 3]; 2 | let br = ar.reduce((acc, el) => acc + el, 10); 3 | let dr = ar.reduce((acc, el) => acc + el * ar[0], 20); 4 | let cr = ar.reduce((acc, el, idx) => acc + el * idx, 0); 5 | console.log(br, dr, cr); 6 | -------------------------------------------------------------------------------- /src/__tests__/specimens/mathMethods/constants.ts: -------------------------------------------------------------------------------- 1 | console.log(Math.E); 2 | console.log(Math.LN2); 3 | console.log(Math.LN10); 4 | console.log(Math.LOG2E); 5 | console.log(Math.LOG10E); 6 | console.log(Math.PI); 7 | console.log(Math.SQRT1_2); 8 | console.log(Math.SQRT2); 9 | -------------------------------------------------------------------------------- /tsfmt.json: -------------------------------------------------------------------------------- 1 | { 2 | "baseIndentSize": 0, 3 | "indentSize": 2, 4 | "tabSize": 2, 5 | "insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces": false, 6 | "placeOpenBraceOnNewLineForFunctions": false, 7 | "placeOpenBraceOnNewLineForControlBlocks": false 8 | } 9 | -------------------------------------------------------------------------------- /src/__tests__/specimens/components/PathResolve/helpers.ts: -------------------------------------------------------------------------------- 1 | export const SOME_CONST = 'buzz'; 2 | 3 | 4 | export function getFoo2() { 5 | return 'foo2'; 6 | } 7 | 8 | export function getFoo() { 9 | return 'foo'; 10 | } 11 | 12 | export const getFoo3 = () => 'foo3'; -------------------------------------------------------------------------------- /src/__tests__/watchSpecimens/modifyFile.1.patch: -------------------------------------------------------------------------------- 1 | --- modifyFile.entry.ts 2 | +++ modifyFile.entry.ts 3 | @@ -4,4 +4,3 @@ const mftest2 = (b: number) => { 4 | }; 5 | const mftest3 = (a: number, b: number) => a + b; 6 | console.log(mftest2(1)); 7 | -console.log(mftest3(1, 2)); 8 | -------------------------------------------------------------------------------- /src/__tests__/watchSpecimens/modifyFile.2.patch: -------------------------------------------------------------------------------- 1 | --- modifyFile.entry.ts 2 | +++ modifyFile.entry.ts 3 | @@ -4,4 +4,3 @@ const mftest2 = (b: number) => { 4 | }; 5 | const mftest3 = (a: number, b: number) => a + b; 6 | console.log(mftest2(1)); 7 | +console.log(mftest3(1, 2)); 8 | -------------------------------------------------------------------------------- /src/__tests__/watchSpecimens/unusedVariable.1.patch: -------------------------------------------------------------------------------- 1 | --- unusedVariable.entry.ts 2 | +++ unusedVariable.entry.ts 3 | @@ -1,5 +1,6 @@ 4 | const uvtest2 = (b: number) => { 5 | let a = 1 + b; 6 | + const c = b; 7 | return a; 8 | }; 9 | console.log(uvtest2(1)); 10 | -------------------------------------------------------------------------------- /src/__tests__/watchSpecimens/unusedVariable.2.patch: -------------------------------------------------------------------------------- 1 | --- unusedVariable.entry.ts 2 | +++ unusedVariable.entry.ts 3 | @@ -1,6 +1,5 @@ 4 | const uvtest2 = (b: number) => { 5 | let a = 1 + b; 6 | - const c = b; 7 | return a; 8 | }; 9 | console.log(uvtest2(1)); 10 | -------------------------------------------------------------------------------- /src/__tests__/watchSpecimens/brokenTsSyntax.1.patch: -------------------------------------------------------------------------------- 1 | --- brokenTsSyntax.entry.ts 2 | +++ brokenTsSyntax.entry.ts 3 | @@ -1,5 +1,5 @@ 4 | const btstest2 = (b: number) => { 5 | - let a = 1 + b; 6 | + let a = <1 + b; 7 | return a; 8 | }; 9 | console.log(btstest2(1)); 10 | -------------------------------------------------------------------------------- /src/__tests__/watchSpecimens/brokenTsSyntax.2.patch: -------------------------------------------------------------------------------- 1 | --- brokenTsSyntax.entry.ts 2 | +++ brokenTsSyntax.entry.ts 3 | @@ -1,5 +1,5 @@ 4 | const btstest2 = (b: number) => { 5 | - let a = <1 + b; 6 | + let a = 1 + b; 7 | return a; 8 | }; 9 | console.log(btstest2(1)); 10 | -------------------------------------------------------------------------------- /src/ts2php/internalConfig/intrinsicElements.ts: -------------------------------------------------------------------------------- 1 | import * as iel from '../../../data/intrinsicElements.json'; 2 | export const intrinsicElements: { [key: string]: string[] } = {}; 3 | for (let i = 0; i < iel.length; i++) { 4 | intrinsicElements[iel[i].tagName] = iel[i].props; 5 | } 6 | -------------------------------------------------------------------------------- /demo/public/index.php: -------------------------------------------------------------------------------- 1 | render([], []), $tpl); 10 | -------------------------------------------------------------------------------- /src/__tests__/specimens/astTools/identities.ts: -------------------------------------------------------------------------------- 1 | function callexp(..._args: any[]) {} 2 | function call2(..._args: any[]]) {} 3 | let ident1, ident2, ident3, ident4, ident5, ident6 = { lol: { kek: 1 }}; 4 | switch (callexp(ident1, ident2, call2(ident3, ident4), typeof ident5, ident6.lol.kek)) {} -------------------------------------------------------------------------------- /src/__tests__/specimens/components/PathResolveReact/Component.tsx: -------------------------------------------------------------------------------- 1 | // @elephizeTarget 2 | export function ReactPathResolveComponent() { 3 | return
foo
; 4 | } 5 | 6 | // @elephizeTarget 7 | export const ReactPathResolveAnonymousComponent = () => { 8 | return
foo
; 9 | } -------------------------------------------------------------------------------- /src/__tests__/watchSpecimens/brokenTsTyping.2.patch: -------------------------------------------------------------------------------- 1 | --- brokenTsTyping.entry.ts 2 | +++ brokenTsTyping.entry.ts 3 | @@ -1,5 +1,5 @@ 4 | const btttest2 = (b: number) => { 5 | - let a: boolean = 1 + b; 6 | + let a = 1 + b; 7 | return a; 8 | }; 9 | console.log(btttest2(1)); 10 | -------------------------------------------------------------------------------- /src/__tests__/watchSpecimens/removeImport.1.patch: -------------------------------------------------------------------------------- 1 | --- removeImport.entry.ts 2 | +++ removeImport.entry.ts 3 | @@ -1,2 +1,4 @@ 4 | -import { inftest1 } from './importNewFileToImport'; 5 | const inftest3 = (b: number) => b + 1; 6 | console.log(inftest3(1)); 7 | -console.log(inftest1(1)); 8 | -------------------------------------------------------------------------------- /src/__tests__/watchSpecimens/importNewFile.1.patch: -------------------------------------------------------------------------------- 1 | --- importNewFile.entry.ts 2 | +++ importNewFile.entry.ts 3 | @@ -1,2 +1,4 @@ 4 | +import { inftest1 } from './importNewFileToImport'; 5 | const inftest2 = (b: number) => b + 1; 6 | console.log(inftest2(1)); 7 | +console.log(inftest1(1)); 8 | -------------------------------------------------------------------------------- /src/__tests__/watchSpecimens/undefVariableUsage.1.patch: -------------------------------------------------------------------------------- 1 | --- undefVariableUsage.entry.ts 2 | +++ undefVariableUsage.entry.ts 3 | @@ -1,5 +1,5 @@ 4 | const uvutest2 = (b: number) => { 5 | let a = 1 + b; 6 | - return a; 7 | + return a + c; 8 | }; 9 | console.log(uvutest2(1)); 10 | -------------------------------------------------------------------------------- /src/__tests__/watchSpecimens/undefVariableUsage.2.patch: -------------------------------------------------------------------------------- 1 | --- undefVariableUsage.entry.ts 2 | +++ undefVariableUsage.entry.ts 3 | @@ -1,5 +1,5 @@ 4 | const uvutest2 = (b: number) => { 5 | let a = 1 + b; 6 | - return a + c; 7 | + return a; 8 | }; 9 | console.log(uvutest2(1)); 10 | -------------------------------------------------------------------------------- /src/ts2php/internalConfig/phpPrettierOptions.ts: -------------------------------------------------------------------------------- 1 | // @ts-ignore 2 | import * as phpPlugin from '@prettier/plugin-php/standalone'; 3 | 4 | export const phpPrettierOptions = { 5 | plugins: [phpPlugin], 6 | parser: 'php', 7 | printWidth: 120, 8 | braceStyle: '1tbs', 9 | } as any; 10 | -------------------------------------------------------------------------------- /src/__tests__/specimens/stringMethods/replace.ts: -------------------------------------------------------------------------------- 1 | let arepl = '12:3:45'; 2 | let brepl = arepl.replace(':', ''); 3 | let crepl = arepl.replaceAll(':', ''); 4 | let drepl = arepl.replace(/[24]/i, 'test'); 5 | let erepl = arepl.replace(/[24]/ig, 'tst'); 6 | console.log(brepl, crepl, drepl, erepl); 7 | -------------------------------------------------------------------------------- /src/__tests__/watchSpecimens/brokenTsTyping.1.patch: -------------------------------------------------------------------------------- 1 | --- brokenTsTyping.entry.ts 2 | +++ brokenTsTyping.entry.ts 3 | @@ -1,5 +1,5 @@ 4 | const btttest2 = (b: number) => { 5 | - let a = 1 + b; 6 | + let a: boolean = 1 + b; 7 | return a; 8 | }; 9 | console.log(btttest2(1)); 10 | 11 | -------------------------------------------------------------------------------- /src/__tests__/watchSpecimens/variableUsage.1.patch: -------------------------------------------------------------------------------- 1 | --- variableUsage.entry.ts 2 | +++ variableUsage.entry.ts 3 | @@ -1,6 +1,6 @@ 4 | const vutest2 = (b: number) => { 5 | let a = 1 + b; 6 | const c = b; 7 | - return a; 8 | + return a + c; 9 | }; 10 | console.log(vutest2(1)); 11 | -------------------------------------------------------------------------------- /src/__tests__/watchSpecimens/variableUsage.2.patch: -------------------------------------------------------------------------------- 1 | --- variableUsage.entry.ts 2 | +++ variableUsage.entry.ts 3 | @@ -1,6 +1,6 @@ 4 | const vutest2 = (b: number) => { 5 | let a = 1 + b; 6 | const c = b; 7 | - return a + c; 8 | + return a; 9 | }; 10 | console.log(vutest2(1)); 11 | -------------------------------------------------------------------------------- /src/ts2php/utils/tsSupportExtensions.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | 3 | // see https://www.typescriptlang.org/tsconfig#include 4 | export const tsSupportExtensions = (compilerOptions: ts.CompilerOptions) => 5 | compilerOptions.allowJs ? ['.js', '.jsx', '.ts', '.tsx'] : ['.ts', '.tsx']; 6 | -------------------------------------------------------------------------------- /src/vendor/php-parser/README: -------------------------------------------------------------------------------- 1 | Copied from https://github.com/mattacosta/php-parser 2 | All rights belong to respective authors. 3 | 4 | Q: Why not npm package? 5 | A: Compiled npm package fails when trying to extend class SyntaxWalker from typescript. Source text in typescript work fine though. 6 | -------------------------------------------------------------------------------- /src/vendor/php-parser/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "declaration": true, 4 | "module": "commonjs", 5 | "outDir": "../lib", 6 | "sourceMap": true, 7 | "strict": true, 8 | "stripInternal": true, 9 | "target": "es2015" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/ts2php/renderers/stdlib/_propName.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | export const propNameIs = (name: string, node: ts.CallExpression) => { 3 | return node.expression.kind === ts.SyntaxKind.PropertyAccessExpression && 4 | (node.expression as ts.PropertyAccessExpression).name.escapedText === name; 5 | }; 6 | -------------------------------------------------------------------------------- /src/ts2php/utils/pathsAndNames/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | camelize, 3 | capitalize, 4 | classNameFromPath, 5 | escapeKeyword, 6 | normalizeBasePath, 7 | normalizeFileExt, 8 | snakify, 9 | normalizeVarName, 10 | } from './utils'; 11 | export { resolveAliasesAndPaths } from './resolveAliasesAndPaths'; 12 | -------------------------------------------------------------------------------- /demo/public/autoload.php: -------------------------------------------------------------------------------- 1 | a.toString() + b.toString(); 5 | +const mfttest = (a: mixed, b: number) => a.toString() + b.toString(); 6 | console.log(mfttest(1, 3)); 7 | -------------------------------------------------------------------------------- /src/__tests__/watchSpecimens/modifyFileTypehints.2.patch: -------------------------------------------------------------------------------- 1 | --- modifyFileTypehints.entry.ts 2 | +++ modifyFileTypehints.entry.ts 3 | @@ -1,2 +1,2 @@ 4 | -const mfttest = (a: mixed, b: number) => a.toString() + b.toString(); 5 | +const mfttest = (a: number, b: number) => a.toString() + b.toString(); 6 | console.log(mfttest(1, 3)); 7 | -------------------------------------------------------------------------------- /src/__tests__/specimens/mathMethods/trigonometry.ts: -------------------------------------------------------------------------------- 1 | let mtSrc = -1.324545234; 2 | let mtSin = Math.sin(mtSrc); 3 | let mtCos = Math.cos(mtSrc); 4 | let mtTan = Math.tan(mtSrc); 5 | let mtAsin = Math.asin(mtSrc); 6 | let mtAcos = Math.acos(mtSrc); 7 | let mtAtan = Math.atan(mtSrc); 8 | console.log(mtSin, mtCos, mtTan, mtAsin, mtAcos, mtAtan); 9 | -------------------------------------------------------------------------------- /src/__tests__/specimens/components/CyclicDeps/index.ts: -------------------------------------------------------------------------------- 1 | import { getCyclicEntry1 } from "./entry1"; 2 | import { getCyclicEntry2, getCyclicEntry3 } from "./entry2"; 3 | 4 | export function getIndex() { 5 | return getCyclicEntry3(); 6 | } 7 | 8 | export { 9 | getCyclicEntry1, 10 | getCyclicEntry2, 11 | getCyclicEntry3, 12 | } 13 | -------------------------------------------------------------------------------- /src/__tests__/specimens/misc/importedTypes.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { TestInterface } from "./exportedTypes"; 3 | 4 | // @elephizeTarget 5 | export const ImportedTypesComponent = ({ lol, flex }: TestInterface & { flex: number }) => { 6 | return
{lol}
; 7 | }; 8 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'reporters': [ 3 | 'jest-tap-reporter' 4 | ], 5 | 'roots': [ 6 | '/src' 7 | ], 8 | testMatch: [ 9 | '**/?(*.)+(spec|test).+(ts|tsx|js)' 10 | ], 11 | 'transform': { 12 | '^.+\\.(ts|tsx)$': 'ts-jest' 13 | }, 14 | 'setupFilesAfterEnv': ['jest-expect-message'] 15 | }; 16 | -------------------------------------------------------------------------------- /src/__tests__/specimens/byType/tsInternals.ts: -------------------------------------------------------------------------------- 1 | const tia = { a: 1, b: 2, c: 3 }; 2 | const tib = { ...tia, d: 4, e: 5 }; 3 | const { c: tic, e: tie, ...others } = tib; 4 | 5 | const tid = [1, 2, 3]; 6 | const tif = [5, ...tid, 6]; 7 | const [tig, tih, ...others2] = tif; 8 | 9 | console.log(tic, tie, others); 10 | console.log(tig, tih, others2); 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | 3 | node_modules/ 4 | .idea/ 5 | demo/node_modules 6 | demo/public/build 7 | demo/public/dist 8 | demo/public/compiled_demo 9 | demo/public/compiled_output.txt 10 | demo/public/compiled_formatted.txt 11 | 12 | # misc 13 | .DS_Store 14 | *.~ 15 | watchSpecimens___ 16 | 17 | npm-debug.log* 18 | yarn-debug.log* 19 | yarn-error.log* 20 | -------------------------------------------------------------------------------- /src/__tests__/specimens/byType/import.ts: -------------------------------------------------------------------------------- 1 | import * as rct from 'react'; 2 | import * as exp from './export'; 3 | import { test, test2 as t2 } from './export'; 4 | // @ts-ignore 5 | import { test as test3 } from '#specimens/byType/export'; 6 | 7 | const f = rct.Fragment; 8 | test(); 9 | test3(); 10 | exp.test(); 11 | let a = t2; 12 | console.log(a, f); 13 | -------------------------------------------------------------------------------- /src/ts2php/renderers/asExpression.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { Declaration } from '../types'; 3 | import { Context } from '../components/context'; 4 | import { renderNode } from '../components/codegen/renderNodes'; 5 | 6 | export const tAsExpression = (node: ts.AsExpression, context: Context) => renderNode(node.expression, context); 7 | -------------------------------------------------------------------------------- /src/__tests__/specimens/components/TypedComponent.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | /** 4 | * @elephizeTarget 5 | * @param props 6 | * @constructor 7 | */ 8 | export function TypedComponent(props: { children: React.ReactNode[]; classes: string }) { 9 | const { children, classes } = props; 10 | return
{children}
; 11 | } -------------------------------------------------------------------------------- /src/ts2php/renderers/spreadElement.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { Declaration } from '../types'; 3 | import { Context } from '../components/context'; 4 | import { renderNode } from '../components/codegen/renderNodes'; 5 | 6 | export const tSpreadElement = (node: ts.SpreadElement, context: Context) => renderNode(node.expression, context); 7 | -------------------------------------------------------------------------------- /src/ts2php/renderers/caseBlock.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { Declaration } from '../types'; 3 | import { Context } from '../components/context'; 4 | import { renderNodes } from '../components/codegen/renderNodes'; 5 | 6 | export const tCaseBlock = (node: ts.CaseBlock, context: Context) => renderNodes([...node.clauses], context).join('\n'); 7 | -------------------------------------------------------------------------------- /src/__tests__/specimens/functionMethods/closures.ts: -------------------------------------------------------------------------------- 1 | const artest = (b: number) => { 2 | let calledTimes = 0; 3 | const nested = (c: number) => { 4 | calledTimes += c; 5 | return calledTimes; 6 | }; 7 | const nested2 = (c: number) => { 8 | return calledTimes + c; 9 | }; 10 | return nested(b) + nested2(b); 11 | }; 12 | 13 | console.log(artest(1)); 14 | -------------------------------------------------------------------------------- /src/ts2php/renderers/spreadAssignment.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { Declaration } from '../types'; 3 | import { Context } from '../components/context'; 4 | import { renderNode } from '../components/codegen/renderNodes'; 5 | 6 | export const tSpreadAssignment = (node: ts.SpreadAssignment, context: Context) => renderNode(node.expression, context); 7 | -------------------------------------------------------------------------------- /external/htmlformat: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | = ba) { 7 | console.log('321'); 8 | } else { 9 | console.log('222'); 10 | } 11 | 12 | if (aa <= ba) { 13 | console.log('123321'); 14 | } 15 | 16 | let obj1 = { a: 1, b: 2 }; 17 | if ('a' in obj1) { 18 | console.log('kek'); 19 | } 20 | -------------------------------------------------------------------------------- /src/__tests__/specimens/components/PathResolve/index.ts: -------------------------------------------------------------------------------- 1 | import { getFoo, SOME_CONST, getFoo2 } from './helpers'; 2 | 3 | export function getBar() { 4 | return 'bar'; 5 | } 6 | 7 | getFoo(); 8 | 9 | const t1 = 't1'; 10 | const t2 = 't2'; 11 | const t3 = 't3'; 12 | 13 | export { 14 | getFoo, 15 | getFoo2, 16 | SOME_CONST, 17 | t1, 18 | t2, 19 | t3 20 | } -------------------------------------------------------------------------------- /src/ts2php/renderers/block.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { Declaration } from '../types'; 3 | import { Context } from '../components/context'; 4 | import { renderNodes } from '../components/codegen/renderNodes'; 5 | 6 | export function tBlock(node: ts.Block, context: Context) { 7 | return ['{', ...renderNodes([...node.statements], context), '}'].join('\n'); 8 | } 9 | -------------------------------------------------------------------------------- /doc/project-description/about-images-edit.txt: -------------------------------------------------------------------------------- 1 | Please use any of PlantUML instances to re-create svg images in this article. 2 | For example: https://plantuml.com/ 3 | 4 | Source code for diagrams can be found in the source of svg files. You may just 5 | copy and paste it to the plantuml web service. Note that '-->' arrows are identified as 6 | comment end in svg markup, so be careful with these arrows. 7 | -------------------------------------------------------------------------------- /src/__tests__/watchSpecimens/elephizeAnnotationTarget.1.patch: -------------------------------------------------------------------------------- 1 | --- elephizeAnnotationTarget.entry.tsx 2 | +++ elephizeAnnotationTarget.entry.tsx 3 | @@ -2,6 +2,7 @@ 4 | 5 | const { useState } = React; 6 | 7 | +// @elephizeTarget 8 | export function ElephizeAnnotationTarget() { // eslint-disable-next-line @typescript-eslint/no-unused-vars 9 | const [count, setCount] = useState(0); 10 | 11 | -------------------------------------------------------------------------------- /src/__tests__/watchSpecimens/elephizeAnnotationTarget.2.patch: -------------------------------------------------------------------------------- 1 | --- elephizeAnnotationTarget.entry.tsx 2 | +++ elephizeAnnotationTarget.entry.tsx 3 | @@ -2,7 +2,6 @@ 4 | 5 | const { useState } = React; 6 | 7 | -// @elephizeTarget 8 | export function ElephizeAnnotationTarget() { // eslint-disable-next-line @typescript-eslint/no-unused-vars 9 | const [count, setCount] = useState(0); 10 | 11 | -------------------------------------------------------------------------------- /src/ts2php/renderers/jsxFragment.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { Declaration } from '../types'; 3 | import { Context } from '../components/context'; 4 | import { renderNodes } from '../components/codegen/renderNodes'; 5 | 6 | export const tJsxFragment = (node: ts.JsxFragment, context: Context) => '$this->frg([\n' + renderNodes([...node.children], context).join(',\n') + '\n])'; 7 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | data/ 2 | external/ 3 | src/ 4 | src/cli.ts 5 | !dist/src/ 6 | !dist/data/ 7 | tsconfig.json 8 | package.json.bak 9 | jest.config.js 10 | elephizerc.example.json 11 | .eslintignore 12 | .eslintrc.js 13 | .gitattributes 14 | 15 | .github/ 16 | .idea/ 17 | demo/ 18 | doc/ 19 | dist/src/__tests__ 20 | 21 | # misc 22 | .DS_Store 23 | 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | -------------------------------------------------------------------------------- /elephizerc.example.json: -------------------------------------------------------------------------------- 1 | { 2 | "aliases": { 3 | "/components/iso_palette": "Components", 4 | "/utils": "Utils" 5 | }, 6 | "bail": "none", 7 | "baseDir": ".", 8 | "noZap": false, 9 | "outDir": "build", 10 | "output": "bootstrap.php", 11 | "quiet": false, 12 | "src": "src/**/*.tsx", 13 | "tsPaths": { 14 | "#aliasedFolder/*": [ 15 | "src/aliased/*" 16 | ] 17 | } 18 | } -------------------------------------------------------------------------------- /src/__tests__/specimens/byType/propertyAccessExpression.ts: -------------------------------------------------------------------------------- 1 | let b1 = { a: 1, b: 2 }; 2 | let d1 = b1['a']; 3 | let d2 = b1.b; 4 | let d3 = { a: { b: { c: 1 }}}; 5 | let d4 = d3.a.b.c.toString(); 6 | let d5 = { a: { b: { c: { d: { e: 2, f: () => 1 } } }}}; 7 | let d6 = d5?.a?.b?.c?.d?.e; 8 | let d7 = d5?.a?.b?.c?.d?.e?.toString(); 9 | let d8 = d5?.a?.b?.c?.d?.f(); 10 | console.log(d1, d2, d4, d6, d7, d8); 11 | -------------------------------------------------------------------------------- /src/ts2php/renderers/variableDeclarationList.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { Declaration } from '../types'; 3 | import { Context } from '../components/context'; 4 | import { renderNodes } from '../components/codegen/renderNodes'; 5 | 6 | export const tVariableDeclarationList = (node: ts.VariableDeclarationList, context: Context) => renderNodes([...node.declarations], context).join('; '); 7 | -------------------------------------------------------------------------------- /src/__tests__/specimens/components/InnerHtmlComponent.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | // @elephizeTarget 4 | export const InnerHtmlComponent = () => { 5 | const inner = 'ololo!'; 6 | return <> 7 |
8 |

This should be dropped

9 |
10 | 11 | ; 12 | }; 13 | -------------------------------------------------------------------------------- /src/__tests__/specimens/misc/typecast.tsx: -------------------------------------------------------------------------------- 1 | let tca = [1, 2, 84]; 2 | let tcb = false; 3 | let tcc = 12; 4 | let tcd = 'asdf'; 5 | let tce = 1234.123; 6 | console.log( 7 | /* @elephizeTypecast array */ tca, 8 | /* @elephizeTypecast bool */ tcb, 9 | /* @elephizeTypecast boolean */ tcb, 10 | /* @elephizeTypecast int */ tcc, 11 | /* @elephizeTypecast string */ tcd, 12 | /* @elephizeTypecast float */ tce 13 | ); -------------------------------------------------------------------------------- /src/ts2php/renderers/parenthesizedExpression.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { Declaration } from '../types'; 3 | import { Context } from '../components/context'; 4 | import { renderNode } from '../components/codegen/renderNodes'; 5 | 6 | export function tParenthesizedExpression(node: ts.ParenthesizedExpression, context: Context) { 7 | return '(' + renderNode(node.expression, context) + ')'; 8 | } 9 | -------------------------------------------------------------------------------- /src/__tests__/specimens/arrayMethods/forEach.ts: -------------------------------------------------------------------------------- 1 | let af = [1, 2, 3]; 2 | af.forEach((el) => console.log(el * 2)); 3 | af.forEach((el, idx) => { 4 | if (el > 1) { 5 | return; 6 | } 7 | af[idx] = el * 2; 8 | }); 9 | af.forEach((el, idx) => af[idx] = el * idx); 10 | 11 | const afo: { [key: string]: number } = { 'a': 1, 'b': 2 }; 12 | Object.keys(afo).forEach((val: string) => { 13 | console.log(afo[val]); 14 | }); 15 | -------------------------------------------------------------------------------- /src/builtins/Functional.php: -------------------------------------------------------------------------------- 1 | { 11 | return
test
; 12 | }; 13 | -------------------------------------------------------------------------------- /src/__tests__/specimens/byType/templateString.ts: -------------------------------------------------------------------------------- 1 | const tsa = 'Hello world'; 2 | const tsb = `Wow ${tsa}`; 3 | const tsc = `Lol ${tsb.length} kek ${tsa.length} wow`; 4 | const tsd = `Kek ${tsc + tsb.substr(1)} lol`; 5 | const tse = `${tsa} wow`; 6 | 7 | console.log(tsd, tse, tsf(true)); 8 | 9 | function tsf(param: boolean) { 10 | if (!param) { 11 | return ''; 12 | } 13 | const aaa = 123; 14 | return `asd ${param} ${aaa} dsa`; 15 | } 16 | -------------------------------------------------------------------------------- /demo/src/render.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { renderToString } from 'react-dom/server'; 3 | import { App } from './components/App.isoentry'; 4 | import fs from 'fs'; 5 | 6 | // @ts-ignore 7 | global.window = { 8 | _elephizeIsServer: true 9 | }; 10 | 11 | const html = fs.readFileSync(__dirname + '/layout.html', { encoding: 'utf-8' }); 12 | const body = renderToString(); 13 | console.log(html.replace('{{placeholder}}', body)); 14 | -------------------------------------------------------------------------------- /src/__tests__/specimens/ToReplace.php: -------------------------------------------------------------------------------- 1 | ) { 7 | const expressions = renderNodes([...node.statements], context); 8 | return `default:\n${expressions.join('\n')}`; 9 | } 10 | -------------------------------------------------------------------------------- /src/__tests__/specimens/misc/defaultValues.ts: -------------------------------------------------------------------------------- 1 | function dvf(a: number, b: number = 123, c: boolean = true) { 2 | console.log(a, b, c); 3 | } 4 | 5 | function dvdo({a, b = 123, c = true}: { a: number, b?: number, c?: boolean }) { 6 | console.log(a, b, c); 7 | } 8 | 9 | function dvda([a, b = 123, c = true]: [number, number?, boolean?]) { 10 | console.log(a, b, c); 11 | } 12 | 13 | console.log(dvf(1)); 14 | console.log(dvdo({ a: 1 })); 15 | console.log(dvda([1])); 16 | -------------------------------------------------------------------------------- /src/__tests__/specimens/byType/jsx.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | /** 4 | * @elephizeTarget 5 | * @constructor 6 | */ 7 | export function Jsx() { 8 | const jsxa = <>
ololotest
test1; 9 | const jsxb = {jsxa}; 10 | 11 | const jsxprops = {id: 'test', className: 'test2'}; 12 | const jsxc =
Oh my {jsxb} my oh
; 13 | return jsxc; 14 | } 15 | -------------------------------------------------------------------------------- /src/__tests__/specimens/components/NullReturnInComponent.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | // @elephizeTarget 4 | export const NullReturnInComponent = (props: { onClick: () => void; count: number }) => { 5 | if (props.count % 2) { 6 | return null; 7 | } 8 | 9 | return ( 10 |
11 |

You clicked {props.count} times

12 | 15 |
16 | ); 17 | }; 18 | -------------------------------------------------------------------------------- /src/__tests__/specimens/components/PathResolveReact/index.tsx: -------------------------------------------------------------------------------- 1 | import { ReactPathResolveComponent, ReactPathResolveAnonymousComponent } from './Component'; 2 | 3 | // @elephizeTarget 4 | export function ReactPathResolveModule() { 5 | return
foo
; 6 | } 7 | 8 | // @elephizeTarget 9 | export const ReactPathResolveAnonymousModule = () => { 10 | return
foo
; 11 | } 12 | 13 | export { 14 | ReactPathResolveComponent, 15 | ReactPathResolveAnonymousComponent 16 | } -------------------------------------------------------------------------------- /src/ts2php/components/typeInference/customTypehintsList.ts: -------------------------------------------------------------------------------- 1 | export const mixedTypehintId = '$__ElephizeTypehint__Mixed__$'; 2 | 3 | export const customTypehints: { [key: string]: { replacement: string; drop: string[] } } = { 4 | // Special one! Checked separately in the code. 5 | [mixedTypehintId]: { 6 | replacement: 'mixed', 7 | drop: ['mixed'], 8 | }, 9 | 10 | '$__ElephizeTypehint__Int__$': { 11 | replacement: 'int', 12 | drop: ['float'], 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | 2 | package.json @VKCOM/vk-sec 3 | package-lock.json @VKCOM/vk-sec 4 | yarn.lock @VKCOM/vk-sec 5 | .yarnrc @VKCOM/vk-sec 6 | .npmrc @VKCOM/vk-sec 7 | workflows/ @VKCOM/vk-sec 8 | .github/ @VKCOM/vk-sec 9 | **/package.json @VKCOM/vk-sec 10 | **/package-lock.json @VKCOM/vk-sec 11 | **/yarn.lock @VKCOM/vk-sec 12 | **/.yarnrc @VKCOM/vk-sec 13 | **/.npmrc @VKCOM/vk-sec 14 | -------------------------------------------------------------------------------- /src/__tests__/specimens/misc/typeUnion.ts: -------------------------------------------------------------------------------- 1 | 2 | // Force 'boolean' in PHPDocs instead of 'bool' 3 | const test1 = 1 as mixed|boolean; 4 | const test2 = 1 as mixed|false; 5 | const test3 = 1 as mixed|true; 6 | const test4 = '' as string|boolean; 7 | const test5 = '' as string|false; 8 | const test6 = '' as string|true; 9 | const test7 = true as mixed|boolean; 10 | 11 | console.log( 12 | test1 13 | , test2 14 | , test3 15 | , test4 16 | , test5 17 | , test6 18 | , test7 19 | ); 20 | -------------------------------------------------------------------------------- /src/ts2php/renderers/caseClause.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { Declaration } from '../types'; 3 | import { Context } from '../components/context'; 4 | import { renderNodes } from '../components/codegen/renderNodes'; 5 | 6 | export function tCaseClause(node: ts.CaseClause, context: Context) { 7 | const [condition, ...expressions] = renderNodes([node.expression, ...node.statements], context); 8 | return `case ${condition}:\n${expressions.join('\n')}`; 9 | } 10 | -------------------------------------------------------------------------------- /src/builtins/StdOutLogFacility.php: -------------------------------------------------------------------------------- 1 | ) { 7 | const [ident, accessor] = renderNodes([node.expression, node.argumentExpression], context); 8 | return `${ident}[${accessor}]`; 9 | } 10 | -------------------------------------------------------------------------------- /src/ts2php/renderers/typeAssertionExpression.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { Declaration } from '../types'; 3 | import { Context } from '../components/context'; 4 | import { renderNode } from '../components/codegen/renderNodes'; 5 | 6 | export function tTypeAssertionExpression(node: ts.TypeAssertion, context: Context) { 7 | // Type assertion is always 3 elements (e.g. <, any, >), and 4th is expression to evaluate: 8 | return renderNode(node.expression, context); 9 | } 10 | -------------------------------------------------------------------------------- /src/__tests__/specimens/components/DummyComponent.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | // @elephizeTarget 4 | export const DummyComponent = (props: { onClick: () => void; count: number }) => { 5 | return ( 6 |
7 |

You clicked {props.count} times

8 | 11 | 14 |
15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /src/__tests__/specimens/byType/JsxModule.php: -------------------------------------------------------------------------------- 1 | acc + v, 0); 11 | } 12 | 13 | console.log(ra1, ra2, ra3, ra4, ra5, rb1, rb2, rb3, rest1, rest2, raf(1, 2, 3, 4)); 14 | -------------------------------------------------------------------------------- /src/__tests__/specimens/byType/enum.ts: -------------------------------------------------------------------------------- 1 | enum NumberedEnum { 2 | 'kek', 3 | 'lol', 4 | 'azaza' 5 | } 6 | 7 | // This should produce an error 8 | enum ForcedNumberedEnum { 9 | 'kek' = 45, 10 | 'lol', 11 | 'azaza' 12 | } 13 | 14 | export enum ValuesBasedEnum { 15 | 'kek' = 'kekvalue', 16 | 'lol' = 'lolvalue', 17 | 'azaza' = 'azazavalue' 18 | } 19 | 20 | const enut1 = NumberedEnum.kek; 21 | const enut2 = ForcedNumberedEnum.azaza; 22 | const enut3 = ValuesBasedEnum.lol; 23 | 24 | console.log(enut1, enut2, enut3); 25 | -------------------------------------------------------------------------------- /src/__tests__/specimens/byType/enum/NumberedEnum.php: -------------------------------------------------------------------------------- 1 | 14 | } 15 | 16 | function getReact2() { 17 | return 18 | } 19 | -------------------------------------------------------------------------------- /src/__tests__/specimens/byType/enum/ForcedNumberedEnum.php: -------------------------------------------------------------------------------- 1 | el * 2); 3 | let cm = am.map((el, idx) => el * idx); 4 | let dm = am.map((el) => el * am[0]); 5 | let em = am.map((el, idx) => { 6 | am[idx] = el * am[idx]; 7 | return el; 8 | }); 9 | let fmc = 0; 10 | let fm = am.map((el) => { 11 | fmc += 1; 12 | fmc++; 13 | --fmc; 14 | return el * am[0]; 15 | }); 16 | let gmc = 0; 17 | let gm = am.map((el) => { 18 | gmc = el + 4; 19 | return el * am[0]; 20 | }); 21 | console.log(bm, cm, dm, em, fm, fmc, gm, gmc); 22 | -------------------------------------------------------------------------------- /src/__tests__/specimens/components/static/KeywordTestComponent.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | const { useState } = React; 4 | 5 | // @elephizeTarget 6 | export function KeywordTestComponent() { 7 | const [count, setCount] = useState(0); 8 | 9 | return ( 10 |
11 |

You clicked {count} times

12 | 15 | 18 |
19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /types/global/index.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable spaced-comment */ 2 | /// 3 | 4 | declare interface ElephizeNodeHook { 5 | run: ( 6 | node: import('../../src/ts2php/types').Node, 7 | context: import('../../src/ts2php/types').IContext 8 | ) => { preventDefault: true; content: string } | { preventDefault: false }; 9 | } 10 | 11 | declare interface ElephizeNodeHookEntry { 12 | nodeKind: import('../../src/ts2php/types').SyntaxKind; 13 | hook: ElephizeNodeHook['run']; 14 | } 15 | -------------------------------------------------------------------------------- /src/__tests__/specimens/byType/enum/ValuesBasedEnum.php: -------------------------------------------------------------------------------- 1 | void, count: number }) { 7 | const { children, ...other } = props; 8 | // @ts-ignore 9 | return window._elephizeIsServer ? 10 | {children} : 11 | {children}; 12 | } 13 | -------------------------------------------------------------------------------- /src/__tests__/watchSpecimens/elephizeAnnotationTarget.entry.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | const { useState } = React; 4 | 5 | export function ElephizeAnnotationTarget() { // eslint-disable-next-line @typescript-eslint/no-unused-vars 6 | const [count, setCount] = useState(0); 7 | 8 | return (
9 |

You clicked {count} times

10 | 14 | 17 |
); 18 | } 19 | -------------------------------------------------------------------------------- /src/__tests__/specimens/mathMethods/basic.ts: -------------------------------------------------------------------------------- 1 | let mSrc = -1.324545234; 2 | let mAbs = Math.abs(mSrc); 3 | let mRnd = Math.round(mSrc); 4 | let mFlr = Math.floor(mSrc); 5 | let mCl = Math.ceil(mSrc); 6 | let mRndm = Math.random(); 7 | let mExp = Math.exp(mSrc); 8 | let mLog = Math.log(mSrc); 9 | let mMax = Math.max(1, 2, 3, 4); 10 | let mMin = Math.min(1, 2, 3, 4); 11 | let mPow = Math.pow(mSrc, mSrc); 12 | let mSqrt = Math.sqrt(mSrc); 13 | let mLog2 = Math.log2(mSrc); 14 | let mLog10 = Math.log10(mSrc); 15 | console.log(mAbs, mRnd, mFlr, mCl, mRndm, mExp, mLog, mMax, mMin, mPow, mSqrt, mLog2, mLog10); 16 | -------------------------------------------------------------------------------- /src/__tests__/specimens/components/ComponentWithOuterFunction.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | const morePrepare = (classes: string) => classes.split(' ').join('-'); 4 | 5 | /** 6 | * @elephizeTarget 7 | * @param props 8 | * @constructor 9 | */ 10 | export function ComponentWithOuterFunction(props: { children: React.ReactNode[]; classes: string }) { 11 | const { children, classes } = props; 12 | return
{children}
; 13 | } 14 | 15 | function prepareClasses(classes: string) { 16 | return classes.split(';').join(' '); 17 | } 18 | -------------------------------------------------------------------------------- /src/__tests__/specimens/components/NestedComponent.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { DummyComponent } from './DummyComponent'; 3 | 4 | const {useState} = React; 5 | 6 | // @elephizeTarget 7 | export function NestedComponent(props: any) { 8 | const [count, setCount] = useState(0); 9 | const onclick = () => { 10 | setCount(count + 1); 11 | }; 12 | const arr = [1, 2, 3]; 13 | 14 | return
15 | setCount(count + 1)} count={count} /> 16 | {arr.map((val) => )} 17 |
; 18 | } 19 | -------------------------------------------------------------------------------- /src/__tests__/specimens/components/BasicComponent.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | const {useState} = React; 4 | 5 | // @elephizeTarget 6 | export function BasicComponent() { // eslint-disable-next-line @typescript-eslint/no-unused-vars 7 | const [count, setCount] = useState(0); 8 | 9 | return ( 10 |
11 |

You clicked {count} times

12 | 15 | 18 |
19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /src/__tests__/specimens/fixes/ExcessiveEscaping.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | interface TextContainerProps { 4 | children: any; // Bug: explicit `any` had lead to excessive escape before the fix 5 | align?: string; 6 | } 7 | 8 | // @elephizeTarget 9 | export const TextContainer = ({ children, align }: TextContainerProps) => { 10 | let classNames = ['content-text--container']; 11 | if (align) { 12 | classNames.push(`content-text--container__align-${align}`); 13 | } 14 | 15 | return ( 16 |
17 | {children} 18 |
19 | ); 20 | }; 21 | -------------------------------------------------------------------------------- /demo/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /demo/src/layout.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | React App 12 | 13 | 14 | 15 |
{{placeholder}}
16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /demo/.elephizerc: -------------------------------------------------------------------------------- 1 | { 2 | "src": ["src/components/**/*.isoentry.tsx"], 3 | "outDir": "public/build", 4 | "quiet": false, 5 | "bail": "none", 6 | "dump": false, 7 | "noZap": false, 8 | "verbose": false, 9 | "verboseUsage": false, 10 | "printImportTree": true, 11 | "rootNs": "", 12 | "builtinsNs": "Builtins", 13 | "baseDir": "src/", 14 | "tsPaths": { 15 | "#iso_palette/*": ["components/iso_palette/*"], 16 | "#utils/*": ["utils/*"] 17 | }, 18 | "aliases": { 19 | "/components/iso_palette": "/Components/iso", 20 | "/components": "/Components", 21 | "/utils": "/Utils" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/__tests__/specimens/components/ComponentBinaryOperators.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | const { useState } = React; 3 | 4 | // @elephizeTarget 5 | export const ComponentBinaryOperators = (props: { onClick: () => void; count: number; initialValue: string| undefined }) => { 6 | const [inputValue] = useState(props.initialValue || ''); 7 | 8 | return ( 9 |
10 |

You clicked {props.count} times

11 | 14 | {inputValue && {inputValue}} 15 |
16 | ); 17 | }; 18 | -------------------------------------------------------------------------------- /src/ts2php/utils/hasExport.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { LogObj } from '../types'; 3 | 4 | export function hasExport(node: ts.Node, log: LogObj) { 5 | if (ts.isVariableStatement(node) && node.modifiers) { 6 | for (const m of node.modifiers) { 7 | if (m.kind === ts.SyntaxKind.DefaultKeyword) { 8 | log.error('Do not use `export default` in transpiled code (and preferably anywhere else). Use named exports.', [], log.ctx(node)); 9 | return null; 10 | } 11 | 12 | if (m.kind === ts.SyntaxKind.ExportKeyword) { 13 | return true; 14 | } 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/__tests__/specimens/components/ImportResolve.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | import { getFoo, getBar } from "./PathResolve"; 4 | import { getFoo as getFoo2 } from "./PathResolve/helpers"; 5 | import { PathResolveReactComponent1 } from "./PathResolveReact/index"; 6 | import { PathResolveReactComponent2 as LolKek } from "./PathResolveReact/Component"; 7 | 8 | import './ImportResolve.css'; 9 | 10 | getBar(); 11 | getFoo(); 12 | getFoo2(); 13 | getReact(); 14 | getReact2(); 15 | 16 | function getReact() { 17 | return 18 | } 19 | 20 | function getReact2() { 21 | return 22 | } 23 | -------------------------------------------------------------------------------- /src/ts2php/components/typeInference/basicTypesMap.ts: -------------------------------------------------------------------------------- 1 | export const typeMap: { [key: string]: string } = { 2 | 'number': 'float', 3 | 'string': 'string', 4 | 'boolean': 'boolean', 5 | 'true': 'boolean', 6 | 'false': 'boolean', 7 | 'any': 'mixed', 8 | }; 9 | 10 | // Force conventional type casts. For example `(bool)` type cast for boolean values (instead of `(boolean)`) 11 | export const typeCastFuncForTypeMap = new Map([ 12 | ['boolean', 'bool'], 13 | ['float', 'float'], 14 | ['string', 'string'], 15 | ]); 16 | 17 | export const typeCastFuncForType = (type: string): string | undefined => typeCastFuncForTypeMap.get(type); 18 | -------------------------------------------------------------------------------- /src/ts2php/utils/pathsAndNames/__tests__/pathsAndNames.spec.ts: -------------------------------------------------------------------------------- 1 | import { normalizeFileExt } from '../utils'; 2 | 3 | describe('normalizeFileExt', () => { 4 | test('replace source extension to .php', () => { 5 | expect(normalizeFileExt('path/to/source.tsx', ['.ts', '.tsx'])).toEqual('path/to/source.php'); 6 | }); 7 | 8 | test('leave only one .php extension', () => { 9 | expect(normalizeFileExt('path/to/source.php.jsx', ['.jsx', '.tsx'])).toEqual('path/to/source.php'); 10 | }); 11 | 12 | test('do not change .php', () => { 13 | expect(normalizeFileExt('path/to/source.php', ['.js', '.jsx'])).toEqual('path/to/source.php'); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /src/ts2php/renderers/whileStatement.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { Declaration } from '../types'; 3 | import { Context } from '../components/context'; 4 | import { isTopLevel } from '../utils/isTopLevel'; 5 | import { renderNodes } from '../components/codegen/renderNodes'; 6 | 7 | export function tWhileStatement(node: ts.WhileStatement, context: Context) { 8 | const [expression, statement] = renderNodes([node.expression, node.statement], context); 9 | const expr = `while (${expression}) ${statement}`; 10 | if (isTopLevel(node, context)) { 11 | context.moduleDescriptor.addStatement(expr); 12 | } 13 | return expr; 14 | } 15 | -------------------------------------------------------------------------------- /demo/src/utils/classnames.ts: -------------------------------------------------------------------------------- 1 | export function classNames(...args: mixed[]): string { 2 | const result: string[] = []; 3 | 4 | args.forEach((item): void => { 5 | if (!item) { 6 | return; 7 | } 8 | 9 | switch (typeof item) { 10 | case 'string': 11 | result.push(item); 12 | break; 13 | 14 | case 'object': 15 | Object.keys(item).forEach((key: string) => { 16 | if ((item as any)[key]) { 17 | result.push(key); 18 | } 19 | }); 20 | break; 21 | 22 | default: 23 | result.push(String(item)); 24 | } 25 | }); 26 | 27 | return result.join(' '); 28 | } 29 | -------------------------------------------------------------------------------- /src/ts2php/renderers/doWhileStatement.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { Declaration } from '../types'; 3 | import { Context } from '../components/context'; 4 | import { isTopLevel } from '../utils/isTopLevel'; 5 | import { renderNodes } from '../components/codegen/renderNodes'; 6 | 7 | export function tDoWhileStatement(node: ts.DoStatement, context: Context) { 8 | const [statement, expression] = renderNodes([node.statement, node.expression], context); 9 | const expr = `do ${statement} while (${expression});`; 10 | if (isTopLevel(node, context)) { 11 | context.moduleDescriptor.addStatement(expr); 12 | } 13 | return expr; 14 | } 15 | -------------------------------------------------------------------------------- /external/check_kphp.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [ -z "$(which docker)" ]; then 4 | echo '[ERROR] Docker not found in distribution. It is required to run kphp checks.'; 5 | exit 0 # it's generally fine, just skip check for this case. 6 | fi 7 | 8 | if ! docker pull vkcom/kphp; then 9 | echo '[ERROR] Docker failed to receive kphp image. Make sure you have sufficient rights (e.g. your user is in the docker group).'; 10 | exit 1 11 | fi 12 | 13 | test -t 1 && USE_TTY="-t" 14 | 15 | docker run ${USE_TTY} -i -v `pwd`/demo/public:/tmp/dev:rw vkcom/kphp /bin/sh -c 'kphp /tmp/dev/index.php -o /tmp/dev/compiled_demo --include-dir "/tmp/dev/build/" --verbosity 3' 16 | exit $? 17 | -------------------------------------------------------------------------------- /src/__tests__/specimens/components/BasicComponentWithProps.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | const {useState} = React; 4 | 5 | interface Props { 6 | count: number; 7 | handleScroll: () => void; 8 | } 9 | 10 | /** 11 | * @elephizeTarget 12 | * @param props 13 | * @constructor 14 | */ 15 | export function BasicComponentWithProps({count: cnt, handleScroll}: Props) { 16 | const [count, setCount] = useState(cnt); 17 | 18 | return ( 19 |
20 |

You clicked {count} times starting at {cnt}.

21 | 24 |
25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /external/check_specimens.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | RED='\033[0;31m' 4 | NC='\033[0m' # No Color 5 | retval=0 6 | 7 | # shellcheck disable=SC2044 8 | for i in $(find src/__tests__/ -type f -iname '*.php'); do 9 | if ! php -l "$i" > /dev/null; 10 | then 11 | retval=1 12 | fi 13 | done 14 | 15 | if [[ "$retval" -eq "1" ]]; then 16 | echo "----------------------------------------------------------------------------------------" 17 | echo -e "${RED}Specimen check failed${NC}: invalid php code found in test data examples. See messages above." 18 | echo "----------------------------------------------------------------------------------------" 19 | fi 20 | 21 | exit $retval 22 | -------------------------------------------------------------------------------- /src/ts2php/components/cli/defaults.ts: -------------------------------------------------------------------------------- 1 | import { CliOptions } from '../../types'; 2 | 3 | export const defaultOptions: CliOptions = { 4 | aliases: {}, 5 | bail: 'none', 6 | baseDir: '.', 7 | config: '.elephizerc', 8 | encoding: 'utf-8', 9 | printImportTree: false, 10 | replaceImports: {}, 11 | ignoreImports: new Set(), 12 | help: false, 13 | noZap: false, 14 | outDir: 'build/', 15 | output: '__stdout', 16 | quiet: false, 17 | rootNs: '\\VK\\Elephize', 18 | src: '**/*.tsx', 19 | tsPaths: {}, 20 | sourceExtensions: ['.ts', '.tsx', '.js', '.jsx'], 21 | verbose: false, 22 | verboseTypehints: false, 23 | verboseUsage: false, 24 | watch: false, 25 | }; 26 | -------------------------------------------------------------------------------- /src/ts2php/components/codegen/defaultCompilerOptions.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import * as path from 'path'; 3 | 4 | export const defaultCompilerOptions: ts.CompilerOptions = { 5 | target: ts.ScriptTarget.ES5, 6 | lib: [ 7 | 'lib.d.ts', 8 | 'lib.dom.d.ts', 9 | 'lib.es5.d.ts', 10 | 'lib.es2015.d.ts', 11 | 'lib.es2016.d.ts', 12 | 13 | path.resolve(__dirname, '..', '..', '..', '..', 'types', 'global', 'compiler-extras.d.ts'), 14 | ], 15 | moduleResolution: ts.ModuleResolutionKind.NodeJs, 16 | module: ts.ModuleKind.CommonJS, 17 | jsx: ts.JsxEmit.React, 18 | allowUnreachableCode: true, 19 | allowJs: true, 20 | resolveJsonModule: true, 21 | }; 22 | -------------------------------------------------------------------------------- /demo/src/server.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import express from 'express'; 3 | import { renderToString } from 'react-dom/server'; 4 | import { App } from './components/App.isoentry'; 5 | import fs from 'fs'; 6 | 7 | const server = express(); 8 | const html = fs.readFileSync(__dirname + '/layout.html', { encoding: 'utf-8' }); 9 | 10 | // @ts-ignore 11 | global.window = { 12 | _elephizeIsServer: false 13 | }; 14 | 15 | server.use(express.static('public')); 16 | 17 | server.get('/', (req, res) => { 18 | const body = renderToString(); 19 | res.send(html.replace('{{placeholder}}', body)); 20 | }); 21 | 22 | server.listen(3000, () => console.log('Example app listening on port 3000!')); 23 | -------------------------------------------------------------------------------- /src/ts2php/renderers/ternaryOperator.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { Declaration } from '../types'; 3 | import { Context } from '../components/context'; 4 | import { renderNode, renderNodes } from '../components/codegen/renderNodes'; 5 | 6 | export function tTernaryOperator(node: ts.ConditionalExpression, context: Context) { 7 | if (node.condition.getText() === 'window._elephizeIsServer') { // special variable for conditional rendering! 8 | return renderNode(node.whenTrue, context); 9 | } 10 | 11 | const [condition, ifTrue, ifFalse] = renderNodes([node.condition, node.whenTrue, node.whenFalse], context, false); 12 | return `${condition} ? ${ifTrue} : ${ifFalse}`; 13 | } 14 | -------------------------------------------------------------------------------- /src/__tests__/specimens/byType/spreadOperator.ts: -------------------------------------------------------------------------------- 1 | const sso = [1, 2, 3, 4]; 2 | const ssb = [2, 3, ...sso, 6]; 3 | const sse = [4, 5, ...sso.map((c) => c * 2), 7]; 4 | 5 | const ssc = { a: 1, b: 2, c: 3 }; 6 | const ssd = { 7 | d: 4, 8 | ...ssc, 9 | e: 5 10 | }; 11 | function ssff(...args: any[]) { 12 | return { ...args, ssd }; 13 | } 14 | const ssf = { 15 | h: 6, 16 | ...ssff(), 17 | j: 8 18 | }; 19 | 20 | const ssg = ssff( 21 | 'Button', 22 | ...ssff('test'), 23 | 'kek', 24 | { 25 | 'lol': 1 26 | } 27 | ); 28 | 29 | const ssgg = ssff( 30 | 'Button', 31 | 'kek', 32 | { 33 | 'lol': 1 34 | }, 35 | ...ssff('test') 36 | ); 37 | 38 | console.log(sso, ssb, ssc, ssd, sse, ssf, ssg, ssgg); 39 | -------------------------------------------------------------------------------- /external/run_compiled_kphp.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [ -z "$(which docker)" ]; then 4 | echo '[ERROR] Docker not found in distribution. It is required to run kphp checks.'; 5 | exit 0 # it's generally fine, just skip check for this case. 6 | fi 7 | 8 | if ! docker pull vkcom/kphp; then 9 | echo '[ERROR] Docker failed to receive kphp image. Make sure you have sufficient rights (e.g. your user is in the docker group).'; 10 | exit 1 11 | fi 12 | 13 | docker run --log-driver none -v `pwd`/demo:/tmp:rw vkcom/kphp /bin/sh -c 'cd /tmp/public && ./compiled_demo --once > compiled_output.txt && chmod 777 compiled_output.txt' 14 | cat demo/public/compiled_output.txt | ./external/htmlformat > demo/public/compiled_formatted.txt 15 | -------------------------------------------------------------------------------- /demo/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/__tests__/specimens/misc/toReplaceIndex/index.ts: -------------------------------------------------------------------------------- 1 | export const getFoo = (test: string, test2: string) => 'foo' + test + test2; 2 | export const getTest1 = (test: string, test2: string = '') => 'foo' + test + test2; 3 | export const getTest2 = (test: string = '', test2: string = '') => 'foo' + test + test2; 4 | export const getTest3 = (test: string = '', test2: string = '', ...args) => 'foo' + test + test2 + args[0]; 5 | export const getTest4 = (test: string = '', test2: string, ...args) => 'foo' + test + test2 + args[0]; 6 | export const getTest5 = (test: string = '', test2: string = '', ...args) => 'foo' + test + test2 + args[0]; 7 | export const getBar = (test: string) => 'bar' + test; 8 | export const classNames: (...args) => string = (..._args) => 'classNames'; -------------------------------------------------------------------------------- /src/ts2php/renderers/stdlib/toString.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { Declaration, ExpressionHook } from '../../types'; 3 | import { propNameIs } from './_propName'; 4 | import { Context } from '../../components/context'; 5 | import { getCallExpressionLeftSide } from '../../utils/ast'; 6 | import { renderNode } from '../../components/codegen/renderNodes'; 7 | 8 | /** 9 | * Anything ().toString() support 10 | * 11 | * @param node 12 | * @param context 13 | */ 14 | export const toString: ExpressionHook = (node: ts.CallExpression, context: Context) => { 15 | if (propNameIs('toString', node)) { 16 | const varNameNode = getCallExpressionLeftSide(node); 17 | return '(string)' + renderNode(varNameNode, context); 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /src/__tests__/specimens/components/PathResolve/TsxModule.php: -------------------------------------------------------------------------------- 1 | eit1 = "123"; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/__tests__/specimens/misc/ExportedTypesModule.php: -------------------------------------------------------------------------------- 1 | just_const = 1; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /.github/workflows/checks.yml: -------------------------------------------------------------------------------- 1 | name: Push checks 2 | 3 | on: [push, workflow_dispatch] 4 | 5 | jobs: 6 | lint: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - uses: actions/checkout@v2 11 | - name: Use Node.js 12.x 12 | uses: actions/setup-node@v1 13 | with: 14 | node-version: '12.x' 15 | - run: yarn install 16 | - run: yarn run lint 17 | 18 | unittest: 19 | runs-on: ubuntu-latest 20 | 21 | steps: 22 | - uses: actions/checkout@v2 23 | - name: Use Node.js 12.x 24 | uses: actions/setup-node@v1 25 | with: 26 | node-version: '12.x' 27 | - name: Use Php 7.4 28 | uses: shivammathur/setup-php@v2 29 | with: 30 | php-version: '7.4' 31 | - run: yarn install 32 | - run: yarn run test_all 33 | -------------------------------------------------------------------------------- /src/__tests__/specimens/misc/AstHooksModule.php: -------------------------------------------------------------------------------- 1 | = new Set([ 2 | 'Infinity', 3 | 'NaN', 4 | 'eval', 5 | 'isFinite', 6 | 'isNaN', 7 | 'parseFloat', 8 | 'parseInt', 9 | 'decodeURI', 10 | 'decodeURIComponent', 11 | 'encodeURI', 12 | 'encodeURIComponent', 13 | 'escape', 14 | 'unescape', 15 | 16 | 'Object', 17 | 'Function', 18 | 'Boolean', 19 | 'Symbol', 20 | 'Error', 21 | 'EvalError', 22 | 'RangeError', 23 | 'ReferenceError', 24 | 'SyntaxError', 25 | 'TypeError', 26 | 'URIError', 27 | 28 | 'Number', 29 | 'Date', 30 | 'String', 31 | 'RegExp', 32 | 'Array', 33 | 'Map', 34 | 'Set', 35 | 'WeakMap', 36 | 'WeakSet', 37 | 'ArrayBuffer', 38 | 'JSON', 39 | 'Promise', 40 | 41 | 'console', 42 | 'document', 43 | ]); 44 | -------------------------------------------------------------------------------- /src/ts2php/renderers/computedPropertyName.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { Declaration } from '../types'; 3 | import { Context } from '../components/context'; 4 | import { renderNode } from '../components/codegen/renderNodes'; 5 | import { getPhpPrimitiveType } from '../components/typeInference/basicTypes'; 6 | 7 | export const tComputedPropertyName = (node: ts.ComputedPropertyName, context: Context) => { 8 | const type = getPhpPrimitiveType(node.expression, context.checker, context.log); 9 | if (type !== 'string') { 10 | context.log.error('Computed property expression type should be inferred as "string", but got "%s" (%s)', 11 | [type, node.expression.getText()], context.log.ctx(node.expression)); 12 | } 13 | return renderNode(node.expression, context); 14 | }; 15 | -------------------------------------------------------------------------------- /src/__tests__/specimens/byType/IdentifierModule.php: -------------------------------------------------------------------------------- 1 | a1 = 1; 26 | \VK\Elephize\Builtins\Console::log($this->a1, $this->a1); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "node", 4 | "lib": ["dom", "es6", "scripthost", "dom.iterable", "es2019"], 5 | "target": "es5", 6 | "experimentalDecorators": true, 7 | "jsx": "react", 8 | "noImplicitAny": true, 9 | "resolveJsonModule": true, 10 | "strictNullChecks": true, 11 | "downlevelIteration": true, 12 | "typeRoots": [ 13 | "./types", 14 | "./node_modules/@types" 15 | ], 16 | "baseUrl": ".", 17 | "outDir": "./dist" 18 | }, 19 | "exclude": [ 20 | "demo/**/*", 21 | "dist/**/*", 22 | "src/__tests__/watchSpecimens.~/**/*", 23 | "src/__tests__/watchSpecimens___/**/*", 24 | "src/__tests__/watchSpecimens/**/*", 25 | "src/__tests__/specimens/**/*", 26 | "node_modules/**/*" 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /src/__tests__/specimens/byType/BinaryExpressionModule.php: -------------------------------------------------------------------------------- 1 | a = 2 + 5; 26 | \VK\Elephize\Builtins\Console::log($this->a); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/ts2php/renderers/exports.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { CommonjsModule } from '../components/cjsModules/commonjsModule'; 3 | import { Context } from '../components/context'; 4 | import { Declaration } from '../types'; 5 | 6 | export function tExportSpecifier(node: ts.ExportSpecifier, context: Context) { 7 | let sourceModule: CommonjsModule | undefined; 8 | const name = node.name.getText(); 9 | 10 | // render nodes 11 | if (context.moduleDescriptor.hasMethod(name)) { 12 | sourceModule = context.moduleDescriptor; 13 | } else { 14 | sourceModule = context.registry.getModuleMethodSource(context.moduleDescriptor, name); 15 | } 16 | 17 | if (sourceModule) { 18 | context.moduleDescriptor.registerExport(sourceModule.sourceFileName, name); 19 | } 20 | 21 | return ''; 22 | } 23 | -------------------------------------------------------------------------------- /src/__tests__/specimens/byType/parameterDestructuring.ts: -------------------------------------------------------------------------------- 1 | function pdf1({ param1: p1, param2: p2, ...other }: { param1: number; param2: string; param3: number; param4: number }, { param1: p3, param2: p4 }: { param1: number; param2: string }, param3: boolean) { 2 | return p1.toString() + p2 + other.param3.toString() + other.param4.toString() + param3.toString() + p3.toString() + p4; 3 | } 4 | 5 | function pdf2([ param1, param2, ...other ]: [ number, string, number, number ], [ param4, param5 ]: [ number, string ], param3: boolean) { 6 | return param1.toString() + param2 + other[0].toString() + other[1].toString() + param3.toString() + param4.toString() + param5; 7 | } 8 | 9 | console.log( 10 | pdf1({ param1: 1, param2: '2', param3: 3, param4: 4 }, { param1: 1, param2: '2' }, false), 11 | pdf2([1, '2', 3, 4], [1, '2'], false) 12 | ); 13 | -------------------------------------------------------------------------------- /src/__tests__/specimens/components/CyclicDeps/Entry1Module.php: -------------------------------------------------------------------------------- 1 | getCyclicEntry2(); 24 | } 25 | 26 | private function __construct() { 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/__tests__/specimens/components/NestedComponentWithDummyInProps.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { DummyComponent } from './DummyComponent'; 3 | 4 | const {useState} = React; 5 | 6 | // @elephizeTarget 7 | export function NestedComponentWithDummyInProps(props: { componentToRender: React.ReactElement }) { 8 | const [count, setCount] = useState(0); 9 | const onclick = () => { 10 | setCount(count + 1); 11 | }; 12 | const arr = [1, 2, 3]; 13 | return
{ props.componentToRender } 14 | setCount(count + 1)} count={count} /> 15 | {arr.map((val) => )} 16 |
; 17 | } 18 | 19 | export function test() { 20 | return {}} count={1} /> } />; 21 | } 22 | -------------------------------------------------------------------------------- /src/__tests__/watchSpecimens/ImportNewFileToImportModule.1.php: -------------------------------------------------------------------------------- 1 | enut4 = \specimens\byType\enum\ValuesBasedEnumEnum::LOL; 26 | \VK\Elephize\Builtins\Console::log($this->enut4); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/ts2php/components/codegen/nodeFlagStore.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { INodeFlagStore, NodeFlags } from '../../types'; 3 | 4 | export class NodeFlagStore implements INodeFlagStore { 5 | private _store: WeakMap; 6 | public constructor() { 7 | this._store = new WeakMap(); 8 | } 9 | 10 | public clear() { 11 | this._store = new WeakMap(); 12 | } 13 | 14 | public get(node: ts.Node): NodeFlags | undefined { 15 | return this._store.get(node); 16 | } 17 | 18 | public upsert(node: ts.Node, flagsToAdd: NodeFlags): NodeFlags { 19 | let val = this._store.get(node); 20 | if (!val) { 21 | val = { ...flagsToAdd }; 22 | } else { 23 | val = { ...val, ...flagsToAdd }; 24 | } 25 | this._store.set(node, val); 26 | return val; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/__tests__/specimens/byType/CallExpressionModule.php: -------------------------------------------------------------------------------- 1 | c = \VK\Elephize\Builtins\document::getElementById("test"); 26 | \VK\Elephize\Builtins\Console::log($this->c); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/__tests__/specimens/components/CyclicDeps/CyclicDepsModule.php: -------------------------------------------------------------------------------- 1 | getCyclicEntry3(); 24 | } 25 | 26 | private function __construct() { 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/__tests__/watchSpecimens/RemoveImportModule.1.php: -------------------------------------------------------------------------------- 1 | inftest3(1)); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/ts2php/renderers/switchStatement.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { Declaration } from '../types'; 3 | import { Context } from '../components/context'; 4 | import { isTopLevel } from '../utils/isTopLevel'; 5 | import { renderNodes } from '../components/codegen/renderNodes'; 6 | import { getIdentities } from '../utils/ast'; 7 | 8 | export function tSwitchStatement(node: ts.SwitchStatement, context: Context) { 9 | const [arg, block] = renderNodes([node.expression, node.caseBlock], context); 10 | getIdentities(node.expression).forEach((usageIdent) => { 11 | context.scope.addUsage(usageIdent.getText(), [], { terminateLocally: true, dryRun: context.dryRun }); 12 | }); 13 | 14 | const expr = `switch (${arg}) {\n${block}\n}`; 15 | if (isTopLevel(node, context)) { 16 | context.moduleDescriptor.addStatement(expr); 17 | } 18 | return expr; 19 | } 20 | -------------------------------------------------------------------------------- /src/__tests__/specimens/components/PathResolve/PathResolveModule.php: -------------------------------------------------------------------------------- 1 | getFoo(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/__tests__/specimens/components/PathResolveReact/ComponentModule.php: -------------------------------------------------------------------------------- 1 | render([], ["foo"]); 24 | } 25 | 26 | private function __construct() { 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/__tests__/specimens/components/elephize_static/KeywordTestModuleModule.php: -------------------------------------------------------------------------------- 1 | mftest2(1)); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /demo/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./public/dist/", 4 | "sourceMap": true, 5 | "noImplicitAny": true, 6 | "module": "commonjs", 7 | "jsx": "react", 8 | "target": "es5", 9 | "lib": [ 10 | "dom", 11 | "dom.iterable", 12 | "esnext" 13 | ], 14 | "allowJs": true, 15 | "skipLibCheck": true, 16 | "esModuleInterop": true, 17 | "allowSyntheticDefaultImports": true, 18 | "strict": true, 19 | "forceConsistentCasingInFileNames": true, 20 | "moduleResolution": "node", 21 | "resolveJsonModule": true, 22 | "baseUrl": "./src/", 23 | "typeRoots": [ 24 | "./node_modules/@types", 25 | "../types" 26 | ], 27 | "paths": { 28 | "#iso_palette/*": ["components/iso_palette/*"], 29 | "#utils/*": ["utils/*"] 30 | } 31 | }, 32 | "include": [ 33 | "src" 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /src/__tests__/specimens/misc/AllowedOnClickModule.php: -------------------------------------------------------------------------------- 1 | Listeners = [ 26 | "foo" => "SomeGlobalVar.foo()", 27 | "bar" => "SomeGlobalVar.bar()", 28 | ]; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/ts2php/renderers/newExpression.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { Context } from '../components/context'; 3 | import { Declaration } from '../types'; 4 | import { flagParentOfType } from '../utils/ast'; 5 | 6 | export const tNewExpression = (node: ts.NewExpression, context: Context) => { 7 | const [file, ident] = context.moduleDescriptor.findImportedIdentifier(node.expression.getText()) || []; 8 | if (!file || !ident || !context.registry.isPlainClass(file, ident)) { 9 | context.log.error('Keyword `new` is supported only for plain classes', [], context.log.ctx(node)); 10 | return ''; 11 | } 12 | 13 | flagParentOfType(node, [ts.SyntaxKind.VariableDeclaration], { 14 | boundClassInstance: context.registry.getPlainClassName(file, ident), 15 | }, context.nodeFlagsStore); 16 | return `new ${context.registry.getPlainClassName(file, ident)}()`; 17 | }; 18 | -------------------------------------------------------------------------------- /src/__tests__/watchSpecimens/BrokenTsSyntaxModule.1.php: -------------------------------------------------------------------------------- 1 | btstest2(1)); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/__tests__/watchSpecimens/VariableUsageModule.2.php: -------------------------------------------------------------------------------- 1 | vutest2(1)); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/__tests__/watchSpecimens/BrokenTsSyntaxModule.2.php: -------------------------------------------------------------------------------- 1 | btstest2(1)); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/__tests__/watchSpecimens/BrokenTsTypingModule.1.php: -------------------------------------------------------------------------------- 1 | btttest2(1)); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/__tests__/watchSpecimens/BrokenTsTypingModule.2.php: -------------------------------------------------------------------------------- 1 | btttest2(1)); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/__tests__/watchSpecimens/UnusedVariableModule.1.php: -------------------------------------------------------------------------------- 1 | uvtest2(1)); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/__tests__/specimens/components/CyclicDeps/Entry2Module.php: -------------------------------------------------------------------------------- 1 | render([], ["foo"]); 24 | } 25 | 26 | private function __construct() { 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/__tests__/specimens/stringMethods/TrimModule.php: -------------------------------------------------------------------------------- 1 | atr = " 12345 "; 30 | $this->btr = trim($this->atr); 31 | \VK\Elephize\Builtins\Console::log($this->btr); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/ts2php/renderers/stdlib/parse.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { Declaration, ExpressionHook } from '../../types'; 3 | import { Context } from '../../components/context'; 4 | import { renderNode } from '../../components/codegen/renderNodes'; 5 | 6 | /** 7 | * parseInt/parseFloat support 8 | * 9 | * @param node 10 | * @param context 11 | */ 12 | export const parse: ExpressionHook = (node: ts.CallExpression, context: Context) => { 13 | if (node.expression.getText() === 'parseInt') { 14 | if (node.arguments.length > 1 && node.arguments[1].getText() !== '10') { 15 | return `intval(${renderNode(node.arguments[0], context)}, ${node.arguments[1].getText()})`; 16 | } 17 | return '(int)' + renderNode(node.arguments[0], context); 18 | } 19 | if (node.expression.getText() === 'parseFloat') { 20 | return '(float)' + renderNode(node.arguments[0], context); 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /src/__tests__/watchSpecimens/UndefVariableUsageModule.2.php: -------------------------------------------------------------------------------- 1 | uvutest2(1)); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/__tests__/watchSpecimens/VariableUsageModule.1.php: -------------------------------------------------------------------------------- 1 | vutest2(1)); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/__tests__/specimens/components/InheritedProps.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | const defaultProps: React.InputHTMLAttributes = { 4 | className: '', 5 | name: undefined, 6 | onChange: undefined, 7 | checked: undefined, 8 | defaultChecked: undefined, 9 | autoFocus: false, 10 | disabled: false, 11 | children: null, 12 | }; 13 | 14 | // @elephizeTarget 15 | export const InheritedProps: React.FunctionComponent> = (inputProps) => { 16 | const props = { ...defaultProps, ...inputProps }; 17 | 18 | const { 19 | className, 20 | children, 21 | ...nativeProps 22 | } = props; 23 | 24 | return ( 25 | 30 | ); 31 | }; 32 | -------------------------------------------------------------------------------- /src/__tests__/watchSpecimens/UndefVariableUsageModule.1.php: -------------------------------------------------------------------------------- 1 | uvutest2(1)); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/__tests__/specimens/components/CyclicDepsEntryModule.php: -------------------------------------------------------------------------------- 1 | getCyclicEntry1(); 21 | \specimens\components\CyclicDeps\Entry2Module::getInstance()->getCyclicEntry2(); 22 | \specimens\components\CyclicDeps\Entry2Module::getInstance()->getCyclicEntry3(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/builtins/Console.php: -------------------------------------------------------------------------------- 1 | write('[log] ' . implode(' ', array_map(function($arg) { 23 | if (is_scalar($arg)) { 24 | return $arg; 25 | } 26 | return json_encode($arg, JSON_PRETTY_PRINT); 27 | }, $args)) 28 | ); 29 | } 30 | 31 | // Use case: add call to error handler 32 | public static function _flush() { 33 | self::$_log_facility->flush(); 34 | } 35 | } 36 | 37 | -------------------------------------------------------------------------------- /src/ts2php/renderers/stdlib/objectKeys.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { Declaration, ExpressionHook } from '../../types'; 3 | import { Context } from '../../components/context'; 4 | import { getCallExpressionArg } from '../../utils/ast'; 5 | import { renderNode } from '../../components/codegen/renderNodes'; 6 | 7 | /** 8 | * Object.keys support 9 | * 10 | * @param node 11 | * @param context 12 | */ 13 | export const objectKeys: ExpressionHook = (node: ts.CallExpression, context: Context) => { 14 | const toCheck = node.expression.kind === ts.SyntaxKind.PropertyAccessExpression && 15 | (node.expression as ts.PropertyAccessExpression).expression.getText() === 'Object' && 16 | (node.expression as ts.PropertyAccessExpression).name.escapedText === 'keys'; 17 | 18 | if (toCheck) { 19 | const varName = renderNode(getCallExpressionArg(node), context); 20 | return `array_keys(${varName})`; 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /src/__tests__/specimens/fixes/EscapeHtmlCharsModule.php: -------------------------------------------------------------------------------- 1 | armenian = "Ձեր"; 30 | $this->injection = ""; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/__tests__/specimens/byType/BlockModule.php: -------------------------------------------------------------------------------- 1 | t1 + 2; 28 | $bb = $aa + 1; 29 | return $bb; 30 | } 31 | 32 | private function __construct() { 33 | $this->t1 = 1; 34 | \VK\Elephize\Builtins\Console::log($this->t()); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/__tests__/specimens/misc/allowedOnClick.tsx: -------------------------------------------------------------------------------- 1 | // @elephizeTarget 2 | export const AllowedGlobalClick = () => { 3 | // @ts-ignore 4 | return
test
; 5 | }; 6 | 7 | // @elephizeTarget 8 | export const AllowedGlobalClickVar = () => { 9 | const variable = 'SomeGlobalVar.test()'; 10 | 11 | // @ts-ignore 12 | return
test
; 13 | }; 14 | 15 | const Listeners = { 16 | foo: 'SomeGlobalVar.foo()', 17 | bar: 'SomeGlobalVar.bar()', 18 | } 19 | 20 | // @elephizeTarget 21 | export const AllowedGlobalClickVar2 = () => { 22 | const variable = Date.now() % 2 ? Listeners.foo : Listeners.bar; 23 | 24 | // @ts-ignore 25 | return
test
; 26 | }; 27 | 28 | 29 | // @elephizeTarget 30 | export const AllowedGlobalClickVar3 = () => { 31 | const variable: 'foo' | 'bar' = 'foo'; 32 | 33 | // @ts-ignore 34 | return
test
; 35 | }; 36 | -------------------------------------------------------------------------------- /src/__tests__/specimens/misc/customIsomorphics.tsx: -------------------------------------------------------------------------------- 1 | import { getLang, getLangStatic } from './toReplace'; 2 | import { getFoo, getBar, classNames, getTest1, getTest2, getTest3, getTest4, getTest5 } from './toReplaceIndex'; 3 | let obj4 = { a: 1, b: 2 }; 4 | do { 5 | // @ts-ignore 6 | console.log(getLang('lol!')); 7 | console.log(getLangStatic('lol!')); 8 | console.log(getFoo('lol!')); 9 | console.log(getBar('lol!')); 10 | console.log(getTest1('lol!')); 11 | console.log(getTest2('lol!', 'lol!')); 12 | console.log(getTest3('lol!')); 13 | console.log(getTest4('lol!', 'lol!')); 14 | console.log(getTest5('lol!')); 15 | if (obj4) break; 16 | } while (true); 17 | 18 | // @elephizeTarget 19 | function IsoComponent() { 20 | return ( 21 |
22 | {/* @ts-ignore */} 23 |
24 |
25 | ) 26 | } 27 | 28 | function r() { 29 | return 30 | } -------------------------------------------------------------------------------- /src/ts2php/renderers/expressionStatement.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { Declaration } from '../types'; 3 | import { Context } from '../components/context'; 4 | import { isTopLevel } from '../utils/isTopLevel'; 5 | import { renderNode } from '../components/codegen/renderNodes'; 6 | 7 | export function tExpressionStatement(node: ts.ExpressionStatement, context: Context) { 8 | const content = renderNode(node.expression, context); 9 | const flags = context.nodeFlagsStore.get(node); 10 | if (flags?.drop || content.length === 0) { 11 | return flags?.dropReplacement || ''; 12 | } 13 | 14 | const additionalExpressions = (flags?.addExpressions || []).join('\n'); 15 | const expr = (additionalExpressions ? additionalExpressions + '\n' : '') + 16 | content + (flags?.passthrough ? '' : ';'); 17 | 18 | if (isTopLevel(node, context)) { 19 | context.moduleDescriptor.addStatement(expr); 20 | } 21 | return expr; 22 | } 23 | -------------------------------------------------------------------------------- /src/ts2php/renderers/stdlib/objectValues.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { Declaration, ExpressionHook } from '../../types'; 3 | import { Context } from '../../components/context'; 4 | import { getCallExpressionArg } from '../../utils/ast'; 5 | import { renderNode } from '../../components/codegen/renderNodes'; 6 | 7 | /** 8 | * Object.values support 9 | * 10 | * @param node 11 | * @param context 12 | */ 13 | export const objectValues: ExpressionHook = (node: ts.CallExpression, context: Context) => { 14 | const toCheck = node.expression.kind === ts.SyntaxKind.PropertyAccessExpression && 15 | (node.expression as ts.PropertyAccessExpression).expression.getText() === 'Object' && 16 | (node.expression as ts.PropertyAccessExpression).name.escapedText === 'values'; 17 | 18 | if (toCheck) { 19 | const varName = renderNode(getCallExpressionArg(node), context); 20 | return `array_values(${varName})`; 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /src/ts2php/renderers/stringLiteral.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { Declaration } from '../types'; 3 | import { escapeHtml, escapeString } from '../utils/escapeString'; 4 | import { flagParentOfType } from '../utils/ast'; 5 | import { Context } from '../components/context'; 6 | import { decode } from 'html-entities'; 7 | 8 | export function tStringLiteral(node: ts.StringLiteral, context: Context) { 9 | if (node.text === 'use strict') { // remove use strict; TODO: can affect random strings containing 'use strict' 10 | flagParentOfType(node, [ts.SyntaxKind.ExpressionStatement], { drop: true }, context.nodeFlagsStore); 11 | } 12 | 13 | if (node.parent.kind === ts.SyntaxKind.JsxAttribute || node.parent.kind === ts.SyntaxKind.JsxExpression) { 14 | // decode html entities for literal strings in jsx 15 | return '"' + escapeHtml(decode(node.text)) + '"'; 16 | } 17 | return '"' + escapeString(node.text) + '"'; 18 | } 19 | -------------------------------------------------------------------------------- /src/ts2php/renderers/this.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { Context } from '../components/context'; 3 | import { Declaration } from '../types'; 4 | import { ClassModule } from '../components/cjsModules/classModule'; 5 | import { flagParentOfType } from '../utils/ast'; 6 | 7 | export const tThis = (node: ts.Node, context: Context) => { 8 | if (context.moduleDescriptor instanceof ClassModule) { 9 | return '$this'; 10 | } else { 11 | // Disallow usage of `this`: we don't support classes and scope binding. 12 | context.log.error('Keyword `this` is not supported for transpilation', [], context.log.ctx(node)); 13 | flagParentOfType(node, [ 14 | ts.SyntaxKind.ExpressionStatement, 15 | ts.SyntaxKind.VariableDeclaration, 16 | ts.SyntaxKind.IfStatement, 17 | ], { drop: true, dropReplacement: '/* ERROR: `this` keyword used in expression */' }, context.nodeFlagsStore); 18 | return ''; 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /src/__tests__/watchSpecimens/ModifyFileTypehintsModule.1.php: -------------------------------------------------------------------------------- 1 | mfttest(1, 3)); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/__tests__/watchSpecimens/ModifyFileTypehintsModule.2.php: -------------------------------------------------------------------------------- 1 | mfttest(1, 3)); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/ts2php/renderers/stdlib/arrayIsArray.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { Declaration, ExpressionHook } from '../../types'; 3 | import { Context } from '../../components/context'; 4 | import { getCallExpressionArg } from '../../utils/ast'; 5 | import { renderNodes } from '../../components/codegen/renderNodes'; 6 | 7 | /** 8 | * Array.isArray support 9 | * 10 | * @param node 11 | * @param context 12 | */ 13 | export const arrayIsArray: ExpressionHook = (node: ts.CallExpression, context: Context) => { 14 | const toCheck = node.expression.kind === ts.SyntaxKind.PropertyAccessExpression && 15 | (node.expression as ts.PropertyAccessExpression).expression.getText() === 'Array' && 16 | (node.expression as ts.PropertyAccessExpression).name.escapedText === 'isArray'; 17 | 18 | if (toCheck) { 19 | const varName = renderNodes([getCallExpressionArg(node)], context); 20 | return `Stdlib::arrayIsArray(${varName})`; 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /src/__tests__/specimens/misc/elephizeIgnore/ComponentIgnore.php: -------------------------------------------------------------------------------- 1 | render([], ["test"]); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/__tests__/specimens/byType/ElementAccessExpressionModule.php: -------------------------------------------------------------------------------- 1 | b = [1, 2, 3]; 30 | $this->d = $this->b[1]; 31 | \VK\Elephize\Builtins\Console::log($this->d); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/__tests__/specimens/byType/ExportModule.php: -------------------------------------------------------------------------------- 1 | test2 = 1; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/__tests__/specimens/components/elephize_static/KeywordTestModuleImportModule.php: -------------------------------------------------------------------------------- 1 | KeywordTestModule(); 24 | } 25 | 26 | private function __construct() { 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/ts2php/utils/regexFlags.ts: -------------------------------------------------------------------------------- 1 | import { LogObj } from '../types'; 2 | import { Node } from 'typescript'; 3 | 4 | export const extractRegexFlags = (regex: string, log: LogObj, ctx: Node) => { 5 | let [matched, expression, flags] = regex.match(/^"\/(.*?)\/([a-zA-Z]+)?"$/) || []; 6 | const output = { 7 | phpFlags: '', 8 | globalSearch: false, 9 | expression, 10 | }; 11 | 12 | if (!matched) { 13 | log.error('Failed to parse regexp: %s', [regex], log.ctx(ctx)); 14 | return output; 15 | } 16 | 17 | if (!flags) { 18 | flags = ''; 19 | } 20 | 21 | if (flags.includes('g')) { 22 | output.globalSearch = true; 23 | flags = flags.replace('g', ''); 24 | } 25 | 26 | if (flags.includes('i')) { 27 | output.phpFlags += 'i'; 28 | flags = flags.replace('i', ''); 29 | } 30 | 31 | if (flags !== '') { 32 | log.error('Unsupported regexp modifiers encountered: %s', [flags], log.ctx(ctx)); 33 | } 34 | 35 | return output; 36 | }; 37 | -------------------------------------------------------------------------------- /src/__tests__/specimens/byType/FunctionDeclarationModule.php: -------------------------------------------------------------------------------- 1 | test1(1)); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/__tests__/specimens/byType/ObjectLiteralExpressionModule.php: -------------------------------------------------------------------------------- 1 | e = [ 26 | "a" => 123, 27 | "b" => 321, 28 | "c" => 222, 29 | "d" => "123", 30 | ]; 31 | \VK\Elephize\Builtins\Console::log($this->e); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/__tests__/specimens/components/TwoComponentsModule.php: -------------------------------------------------------------------------------- 1 | default_props = [ 26 | "onClick" => /* anon_e8c52de */ function () { 27 | return; 28 | }, 29 | "initialValue" => "", 30 | "count" => 0, 31 | ]; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/__tests__/specimens/byType/switchStatement.ts: -------------------------------------------------------------------------------- 1 | let as: any = { v: 1 }; 2 | 3 | switch (as.v) { 4 | case 1: 5 | console.log('lol'); 6 | break; 7 | case '1': 8 | console.log('omg'); 9 | // break intentionally omitted 10 | case 2: 11 | console.log('kek'); 12 | break; 13 | default: 14 | console.log('wow'); 15 | } 16 | 17 | export function classNames(...args: mixed[]): string { 18 | const result: string[] = []; 19 | 20 | args.forEach((item): void => { 21 | if (!item) { 22 | return; 23 | } 24 | 25 | switch (typeof item) { 26 | case 'string': 27 | result.push(item); 28 | break; 29 | 30 | case 'object': 31 | Object.keys(item).forEach((key: string) => { 32 | if ((item as any)[key]) { 33 | result.push(key); 34 | } 35 | }); 36 | break; 37 | 38 | default: 39 | result.push(String(item)); 40 | } 41 | }); 42 | 43 | return result.join(' '); 44 | } 45 | -------------------------------------------------------------------------------- /src/__tests__/8_customMisc.spec.ts: -------------------------------------------------------------------------------- 1 | import { runBatch } from './runBatch'; 2 | import { configureLogging } from '../ts2php/components/cli/configureLogging'; 3 | import * as path from 'path'; 4 | import { SyntaxKind, NumericLiteral } from 'typescript'; 5 | 6 | const log = configureLogging({ 7 | baseDir: path.resolve(__dirname, 'specimens'), outDir: '', 8 | }); 9 | 10 | test('ts2php.customMisc', () => { 11 | return runBatch([__dirname, 'specimens'], [ 12 | ['misc', 'allowedOnClick.tsx'], 13 | ['misc', 'astHooks.tsx'], 14 | ], log, { 15 | jsxPreferences: { 16 | allowStringEvents: true, 17 | }, 18 | hooks: { 19 | [SyntaxKind.NumericLiteral]: { 20 | run: (node: NumericLiteral) => { 21 | if (node.getText().endsWith('.')) { 22 | return { preventDefault: true, content: `1 + ${node.getText()}` }; 23 | } else { 24 | return { preventDefault: false }; 25 | } 26 | }, 27 | }, 28 | }, 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /src/__tests__/watchSpecimens/ImportNewFileModule.1.php: -------------------------------------------------------------------------------- 1 | inftest2(1)); 29 | \VK\Elephize\Builtins\Console::log(\watchSpecimens___\ImportNewFileToImportModule::getInstance()->inftest1(1)); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/ts2php/renderers/templateString.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { Declaration } from '../types'; 3 | import { escapeString } from '../utils/escapeString'; 4 | import { Context } from '../components/context'; 5 | import { renderNodes } from '../components/codegen/renderNodes'; 6 | 7 | export const tTemplateExpression = (node: ts.TemplateExpression, context: Context) => { 8 | return renderNodes([node.head, ...node.templateSpans], context).join(' . '); 9 | }; 10 | 11 | export function tTemplateSpan(node: ts.TemplateSpan, context: Context) { 12 | const [expr, literal] = renderNodes([node.expression, node.literal], context); 13 | return `(${expr})` + (literal && literal !== '""' ? ' . ' + literal : ''); 14 | } 15 | 16 | export function tTemplateStatic(node: ts.TemplateHead | ts.TemplateMiddle | ts.TemplateTail | ts.NoSubstitutionTemplateLiteral) { 17 | const str = escapeString(node.text); 18 | if (str) { 19 | return '"' + str + '"'; 20 | } 21 | return ''; 22 | } 23 | -------------------------------------------------------------------------------- /src/__tests__/specimens/misc/unusedVarsElimination.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 2 | const uve = 3; // should be eliminated 3 | export const uv = 1; // should not eliminate exports 4 | const uvctx = 1; // used in lower context and should not be eliminated 5 | export function uvfun() { 6 | const a = uvctx; // used 7 | const b = a + 2; // unused and should be eliminated (elimination of const c makes it so) 8 | const c = b + 3; // unused and should be eliminated (elimination of const e makes it so) 9 | const d = a; 10 | console.log(d); 11 | const e = uvefun(c); // unused and should be eliminated (elimination of const f makes it so) 12 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 13 | const f = e; 14 | const g = uvctx; 15 | return g; 16 | } 17 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 18 | function uvefun(s: number) { // unused and should be eliminated (elimination of const e makes it so) 19 | console.log('azaza'); 20 | return 11; 21 | } 22 | -------------------------------------------------------------------------------- /src/__tests__/specimens/objectMethods/KeysModule.php: -------------------------------------------------------------------------------- 1 | ok = [ 30 | 1 => 123, 31 | "test" => 321, 32 | 23 => 1111, 33 | ]; 34 | $this->okkeys = array_keys($this->ok); 35 | \VK\Elephize\Builtins\Console::log($this->okkeys); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/ts2php/components/unusedCodeElimination/usageGraph/nodeData.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { Scope } from './index'; 3 | import { sha1 } from '../../../utils/sha1'; 4 | import { Declaration } from '../../../types'; 5 | 6 | export function insideComponent(scope: Scope) { 7 | while (scope) { 8 | if (scope.ownerNode?.data.isComponent) { 9 | return true; 10 | } 11 | scope = scope.parentScope!; 12 | } 13 | 14 | return false; 15 | } 16 | 17 | export function usedInNestedScope(decl: Declaration | undefined, declScope: Scope, currentScope: Scope): boolean { 18 | const flags = decl?.flags || {}; 19 | const noOtherFlags = !(flags.External) && !(flags.Local) && !(flags.DereferencedImport) && !(flags.HoistedToModule); 20 | return !!decl && !currentScope.isRoot() && declScope !== currentScope && noOtherFlags; 21 | } 22 | 23 | export function identifyAnonymousNode(node: ts.Node): string { 24 | return 'anon_' + sha1(node.getText()).substr(0, 7); 25 | } 26 | -------------------------------------------------------------------------------- /src/ts2php/renderers/typeofExpression.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { Declaration } from '../types'; 3 | import { Context } from '../components/context'; 4 | import { renderNode } from '../components/codegen/renderNodes'; 5 | 6 | export function tTypeofExpression(node: ts.TypeOfExpression, context: Context) { 7 | const type = context.checker.getTypeAtLocation(node.expression); 8 | const evaluatedType = context.checker.typeToString(type, node.expression, ts.TypeFormatFlags.None); 9 | if (type.getCallSignatures().length === 0 && evaluatedType !== 'any' && evaluatedType !== 'unknown') { 10 | const exp = renderNode(node.expression, context); 11 | return `Stdlib::typeof(${exp})`; 12 | } 13 | 14 | context.log.error('Typeof operator does not support `any`/`unknown` and functional arguments ' + 15 | '(and expressions returning functional types). Ensure that your expression evaluates to ' + 16 | 'non-callable explicit type.', [], context.log.ctx(node)); 17 | return 'null'; 18 | } 19 | -------------------------------------------------------------------------------- /src/__tests__/specimens/arrayMethods/PushPopModule.php: -------------------------------------------------------------------------------- 1 | a_st = [1, 2, 3]; 30 | array_push($this->a_st, 3); 31 | array_push($this->a_st, 1, 2, 3); 32 | $this->b_st = array_pop($this->a_st); 33 | \VK\Elephize\Builtins\Console::log($this->b_st); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/__tests__/specimens/byType/ImportModule.php: -------------------------------------------------------------------------------- 1 | test(); 26 | \specimens\byType\ExportModule::getInstance()->test(); 27 | \specimens\byType\ExportModule::getInstance()->test(); 28 | $this->a = \specimens\byType\ExportModule::getInstance()->test2; 29 | \VK\Elephize\Builtins\Console::log($this->a, $f); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/__tests__/specimens/components/PathResolveReact/index/ReactPathResolveModule.php: -------------------------------------------------------------------------------- 1 | render([], ["foo"]); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/__tests__/specimens/objectMethods/ValuesModule.php: -------------------------------------------------------------------------------- 1 | ok = [ 30 | 1 => 123, 31 | "test" => 321, 32 | 23 => 1111, 33 | ]; 34 | $this->okvals = array_values($this->ok); 35 | \VK\Elephize\Builtins\Console::log($this->okvals); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/__tests__/specimens/byType/ClassesUseInExternalModule.php: -------------------------------------------------------------------------------- 1 | c = new \specimens\byType\classes\ClassesClass(); 26 | $this->c->setter1(123); 27 | \VK\Elephize\Builtins\Console::log( 28 | $this->c->getter1() + \specimens\byType\classes\ClassesClass::MY_VAR 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/ts2php/renderers/arrayLiteralExpression.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { Declaration } from '../types'; 3 | import { Context } from '../components/context'; 4 | import { renderNode, renderNodes } from '../components/codegen/renderNodes'; 5 | 6 | export function tArrayLiteralExpression(node: ts.ArrayLiteralExpression, context: Context) { 7 | let synList: string[] = []; 8 | let toRender: ts.Node[] = []; 9 | 10 | for (let i = 0; i < node.elements.length; i++) { 11 | if (node.elements[i].kind === ts.SyntaxKind.SpreadElement) { 12 | synList = synList.concat('[' + renderNodes(toRender, context).join(', ') + ']'); 13 | toRender = []; 14 | synList.push(renderNode(node.elements[i], context)); 15 | } else { 16 | toRender.push(node.elements[i]); 17 | } 18 | } 19 | synList = synList.concat('[' + renderNodes(toRender, context).join(', ') + ']'); 20 | 21 | if (synList.length === 1) { 22 | return synList[0]; 23 | } 24 | 25 | return `array_merge(${synList.join(', ')})`; 26 | } 27 | -------------------------------------------------------------------------------- /src/__tests__/specimens/components/PathResolveReact/Component/PathResolveReactComponent2.php: -------------------------------------------------------------------------------- 1 | render([], []); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/__tests__/specimens/components/PathResolveReact/Component/ReactPathResolveComponent.php: -------------------------------------------------------------------------------- 1 | render([], ["foo"]); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/__tests__/specimens/components/elephize_static/KeywordTestComponentImportModule.php: -------------------------------------------------------------------------------- 1 | render( 24 | [], 25 | [] 26 | ); 27 | } 28 | 29 | private function __construct() { 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/__tests__/specimens/misc/allowedOnClick/AllowedGlobalClickVar3.php: -------------------------------------------------------------------------------- 1 | render(["onClick" => $variable], ["test"]); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/__tests__/specimens/misc/allowedOnClick/AllowedGlobalClick.php: -------------------------------------------------------------------------------- 1 | render( 29 | ["onClick" => "SomeGlobalVar.test()"], 30 | ["test"] 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/__tests__/specimens/misc/allowedOnClick/AllowedGlobalClickVar.php: -------------------------------------------------------------------------------- 1 | render(["onClick" => $variable], ["test"]); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/__tests__/specimens/stringMethods/JoinModule.php: -------------------------------------------------------------------------------- 1 | ajn = ["12", "3", "45"]; 34 | $this->bjn = implode(":", $this->ajn); 35 | $this->cjn = implode($this->ajn); 36 | \VK\Elephize\Builtins\Console::log($this->bjn, $this->cjn); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/__tests__/specimens/stringMethods/ParseModule.php: -------------------------------------------------------------------------------- 1 | pria = (int) "123"; 34 | $this->prib = intval("231", 16); 35 | $this->prfa = (float) "123.23"; 36 | \VK\Elephize\Builtins\Console::log($this->pria, $this->prib, $this->prfa); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/ts2php/renderers/variableStatement.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { Declaration } from '../types'; 3 | import { Context } from '../components/context'; 4 | import { hasExport } from '../utils/hasExport'; 5 | import { renderNode } from '../components/codegen/renderNodes'; 6 | 7 | export function tVariableStatement(node: ts.VariableStatement, context: Context) { 8 | const exported = hasExport(node, context.log); 9 | const declList = node.declarationList; 10 | 11 | if (exported) { 12 | renderNode(declList, context); 13 | const flags = context.nodeFlagsStore.get(node); 14 | 15 | return [ 16 | ...flags?.addExpressions || [], 17 | ].join('\n'); 18 | } 19 | 20 | const content = renderNode(declList, context); 21 | if (!content || content.length === 0) { 22 | return ''; 23 | } 24 | 25 | const flags = context.nodeFlagsStore.get(node); 26 | const additionalExpressions = (flags?.addExpressions || []).join('\n'); 27 | return (additionalExpressions ? additionalExpressions + '\n' : '') + content + ';'; 28 | } 29 | -------------------------------------------------------------------------------- /src/__tests__/specimens/components/PathResolveReact/index/ReactPathResolveAnonymousModule.php: -------------------------------------------------------------------------------- 1 | render([], ["foo"]); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/__tests__/specimens/stringMethods/SubstrModule.php: -------------------------------------------------------------------------------- 1 | asub = "12345"; 34 | $this->bsub = substr($this->asub, 2); 35 | $this->csub = substr($this->asub, 2, 2); 36 | \VK\Elephize\Builtins\Console::log($this->bsub, $this->csub); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/ts2php/renderers/stdlib/stringTrim.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { Declaration, ExpressionHook } from '../../types'; 3 | import { propNameIs } from './_propName'; 4 | import { hasType } from '../../components/typeInference/basicTypes'; 5 | import { Context } from '../../components/context'; 6 | import { getCallExpressionLeftSide } from '../../utils/ast'; 7 | import { renderNode } from '../../components/codegen/renderNodes'; 8 | 9 | /** 10 | * String.prototype.trim support 11 | * 12 | * @param node 13 | * @param context 14 | */ 15 | export const stringTrim: ExpressionHook = (node: ts.CallExpression, context: Context) => { 16 | if (propNameIs('trim', node)) { 17 | if (!hasType(node.expression, context.checker, 'string')) { 18 | context.log.error('Left-hand expression must have string inferred type', [], context.log.ctx(node)); 19 | return 'null'; 20 | } 21 | const varNameNode = getCallExpressionLeftSide(node); 22 | const varName = renderNode(varNameNode, context); 23 | return `trim(${varName})`; 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /src/__tests__/specimens/mathMethods/ConstantsModule.php: -------------------------------------------------------------------------------- 1 | hopobj = [ 30 | 1 => 123, 31 | "test" => 321, 32 | 23 => 1111, 33 | ]; 34 | $this->hop = array_key_exists("test", $this->hopobj); 35 | \VK\Elephize\Builtins\Console::log($this->hop); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/__tests__/specimens/components/NestedConditionalComponent/ComponentWrapper.php: -------------------------------------------------------------------------------- 1 | render($other, [$children]); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/__tests__/specimens/components/PathResolveReact/Component/ReactPathResolveAnonymousComponent.php: -------------------------------------------------------------------------------- 1 | render([], ["foo"]); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/ts2php/renderers/stdlib/typecastConstructors.ts: -------------------------------------------------------------------------------- 1 | import * as ts from 'typescript'; 2 | import { Declaration, ExpressionHook } from '../../types'; 3 | import { Context } from '../../components/context'; 4 | import { getCallExpressionArg } from '../../utils/ast'; 5 | import { renderNode } from '../../components/codegen/renderNodes'; 6 | 7 | /** 8 | * Support for type casting using String(), Number() and Boolean() 9 | * 10 | * @param node 11 | * @param context 12 | */ 13 | export const typecastConstructors: ExpressionHook = (node: ts.CallExpression, context: Context) => { 14 | const toCheck = node.expression.kind === ts.SyntaxKind.Identifier && 15 | ['Number', 'String', 'Boolean'].includes(node.expression.getText()); 16 | 17 | if (toCheck) { 18 | const varName = renderNode(getCallExpressionArg(node), context); 19 | switch (node.expression.getText()) { 20 | case 'Number': 21 | return `+${varName}`; 22 | case 'String': 23 | return `(string)${varName}`; 24 | case 'Boolean': 25 | return `(boolean)${varName}`; 26 | } 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /src/__tests__/specimens/arrayMethods/IncludesModule.php: -------------------------------------------------------------------------------- 1 | ain = [1, 2, 3]; 34 | $this->bin = in_array(2, $this->ain, true); 35 | $this->cin = in_array(2, array_slice($this->ain, 1), true); 36 | \VK\Elephize\Builtins\Console::log($this->bin, $this->cin); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/__tests__/specimens/components/ReexportResolve.tsx: -------------------------------------------------------------------------------- 1 | import { getFoo, SOME_CONST, getBar as getBuzz, getFoo2 } from "./PathResolve"; 2 | import { ReactPathResolveAnonymousModule, ReactPathResolveComponent, ReactPathResolveModule } from "./PathResolveReact"; 3 | import { ReactPathResolveAnonymousComponent, ReactPathResolveComponent as ReactPathResolveComponentDirect } from "./PathResolveReact/Component"; 4 | import { ReactPathResolveComponent as ReactPathResolveComponentAlias } from "#specimens/components/PathResolveReact"; 5 | import { getFoo3 } from "./PathResolve/helpers"; 6 | 7 | getFoo(); 8 | getFoo2(); 9 | getFoo3(); 10 | getBuzz(); 11 | console.log(SOME_CONST); 12 | 13 | // @elephizeTarget 14 | function render() { 15 | getFoo(); 16 | getFoo2(); 17 | getFoo3(); 18 | getBuzz(); 19 | console.log(SOME_CONST); 20 | 21 | return <> 22 | 23 | 24 | 25 | 26 | 27 | 28 | ; 29 | } -------------------------------------------------------------------------------- /src/__tests__/specimens/misc/ComplexObjectFuncsModule.php: -------------------------------------------------------------------------------- 1 | default_props = [ 26 | "className" => "", 27 | "name" => null, 28 | "onChange" => null, 29 | "checked" => null, 30 | "defaultChecked" => null, 31 | "autoFocus" => false, 32 | "disabled" => false, 33 | "children" => null, 34 | ]; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/__tests__/specimens/stringMethods/MatchModule.php: -------------------------------------------------------------------------------- 1 | amat = "12:3:45"; 34 | $this->dmat = Stdlib::strMatch("/[24]/iu", $this->amat); 35 | $this->emat = Stdlib::strMatchG("/[24]/iu", $this->amat); 36 | \VK\Elephize\Builtins\Console::log($this->dmat, $this->emat); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/__tests__/specimens/stringMethods/StartsWithModule.php: -------------------------------------------------------------------------------- 1 | astw = "12345"; 34 | $this->bstw = strpos($this->astw, "1") === 0; 35 | $this->cstw = strpos($this->astw, "2", 1) === 0; 36 | \VK\Elephize\Builtins\Console::log($this->bstw, $this->cstw); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/vendor/php-parser/parser/ModifierFlags.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 Matt Acosta 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | 'use strict'; 18 | 19 | /** 20 | * Specifies the modifier keywords parsed prior to a type member declaration. 21 | */ 22 | export enum ModifierFlags { 23 | 24 | None = 0, 25 | 26 | Abstract = 1 << 0, 27 | 28 | Final = 1 << 1, 29 | 30 | Private = 1 << 2, 31 | 32 | Protected = 1 << 3, 33 | 34 | Public = 1 << 4, 35 | 36 | Static = 1 << 5, 37 | 38 | VisibilityMask = Private | Protected | Public, 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/__tests__/specimens/byType/ServerIfStatementModule.php: -------------------------------------------------------------------------------- 1 | sis_aa = "test1"; 34 | $this->sis_ab = "test2"; 35 | $this->sis_ac = $this->sis_aa; 36 | \VK\Elephize\Builtins\Console::log($this->sis_ab, $this->sis_aa, $this->sis_ac); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/__tests__/specimens/stringMethods/StrIncludesModule.php: -------------------------------------------------------------------------------- 1 | ainc = "12345"; 34 | $this->binc = strpos($this->ainc, "2") !== false; 35 | $this->cinc = strpos($this->ainc, "2", 2) !== false; 36 | \VK\Elephize\Builtins\Console::log($this->binc, $this->cinc); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /demo/src/components/iso_palette/Radio/Radio.iso.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { classNames as cx } from '#utils/classnames'; 3 | 4 | const defaultProps: React.InputHTMLAttributes = { 5 | className: '', 6 | name: undefined, 7 | onChange: undefined, 8 | checked: undefined, 9 | defaultChecked: undefined, 10 | autoFocus: false, 11 | disabled: false, 12 | children: null, 13 | }; 14 | 15 | // @elephizeTarget 16 | export const Radio: React.FunctionComponent> = (inputProps) => { 17 | const props = { ...defaultProps, ...inputProps }; 18 | 19 | const { 20 | className, 21 | children, 22 | ...nativeProps 23 | } = props; 24 | 25 | const classNames = cx( 26 | 'Radio', 27 | className, 28 | { 'Radio--disabled': !!props.disabled }, 29 | ); 30 | 31 | return ( 32 | 37 | ); 38 | }; 39 | -------------------------------------------------------------------------------- /src/__tests__/specimens/components/ComponentWithOuterFunctionModule.php: -------------------------------------------------------------------------------- 1 | { 23 | 24 | /** 25 | * Determines if the current position in the tree is a token (i.e. a leaf). 26 | */ 27 | readonly isToken: boolean; 28 | 29 | /** 30 | * The parent node of this object. 31 | */ 32 | readonly parent: T | null; 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/vendor/php-parser/text/Encoding.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 Matt Acosta 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | 'use strict'; 18 | 19 | export enum Encoding { 20 | 21 | /** 22 | * A string containing ISO-8859-1 characters, encoded using one byte. 23 | */ 24 | Latin1, 25 | /** 26 | * A string containing Unicode characters, encoded using one to four bytes. 27 | */ 28 | Utf8, 29 | /** 30 | * A string containing Unicode characters, encoded using two or four bytes in 31 | * little-endian order. 32 | */ 33 | Utf16le, 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/__tests__/watchSpecimens/ModifyFileModule.2.php: -------------------------------------------------------------------------------- 1 | mftest2(1)); 38 | \VK\Elephize\Builtins\Console::log($this->mftest3(1, 2)); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/vendor/php-parser/parser/DocumentationMode.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 Matt Acosta 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | 'use strict'; 18 | 19 | /** 20 | * Specifies how documentation comments should be parsed. 21 | */ 22 | export enum DocumentationMode { 23 | 24 | /** 25 | * Do not parse documentation comments. 26 | */ 27 | None, 28 | /** 29 | * Parse documentation comments but do not report any diagnostics. 30 | */ 31 | Parse, 32 | /** 33 | * Parse documentation comments and report diagnostics. 34 | */ 35 | Diagnose, 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/__tests__/specimens/byType/ArrowFunctionModule.php: -------------------------------------------------------------------------------- 1 | test2(1)); 38 | \VK\Elephize\Builtins\Console::log($this->test3(1, 2)); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/__tests__/specimens/components/TypedComponent/TypedComponent.php: -------------------------------------------------------------------------------- 1 | render( 30 | ["className" => \VK\Elephize\Builtins\IntrinsicElement::escape($classes)], 31 | [$children] 32 | ); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/builtins/RenderableComponent.php: -------------------------------------------------------------------------------- 1 | flatten($rendered_components)); 21 | } 22 | 23 | /** 24 | * @param array $array 25 | * @return string[] 26 | */ 27 | protected function flatten($array) { 28 | /** @var string[] $flat */ 29 | $flat = []; 30 | 31 | foreach ($array as $item) { 32 | if (is_array($item)) { 33 | $flat = array_merge($flat, $this->flatten($item)); 34 | } else if ($item !== null) { 35 | $flat[] = (string)$item; 36 | } 37 | } 38 | 39 | return $flat; 40 | } 41 | } 42 | --------------------------------------------------------------------------------