На данный момент поддерживаются базовые способы разбиения кода на модули в стиле ES6, а именно:
export function() {...}
export const = function() {...}
export const = () => {...}
export const = { ... }
— для объектов-данных. Не следует запихивать в объекты какие бы то ни было функции и колбэки, т.к. kphp с большой вероятностью не сможет вывести их тип.export { getFoo, getBar }
import { Some } from '../modules/some'
import { Some as SomeOther } from '../modules/some'
import * as React from 'react'
— пока что это костыль именно для React. Для остальных модулей лучше использовать именованные импорты.
НЕ поддерживаются:
- Всё, что связано с CommonJS
require()
export default [что бы то ни было]
export Some from '../modules/some
import Some from '../modules/some
import Some = require(...)
При преобразовании кода требуется выполнить несколько простых правил:
- Отсутствие кода в глобальной области.
- Один класс - один файл, имя класса == имя файла.
Это ведет нас к тому, что:
- Каждый CommonJS файл должен на стороне php представляться отдельным классом.
- Кроме того, логично сделать React-компоненты отдельными классами с общим предком, поскольку у них есть некоторые общие функции, связанные с рендером.
- Следовательно, нужно уметь извлекать классы React-компонентов из CommonJS-классов в отдельные файлы и разруливать зависимости между ними.
- Все переменные и функции, объявленные в глобальной области видимости, становятся методами и свойствами класса. Присваивание значений переменных и прочий императивный код выносятся в конструктор класса.
- Фактически, модификатор доступа для всех свойств и методов выставляется в
public
. Это поведение может быть изменено в будущих версиях и не следует использовать во внешнем коде свойства и методы, не помеченные модификаторомexport
в исходном ts-коде. - Вложенные функции (т.е. объявленные не в глобальном модуле) превращаются в функциональные выражения и в них пробрасываются необходимые переменные через
use()
. - Импорты других модулей превращаются в инструкции require_once с относительными путями.
- Если функция в глобальной области модуля помечена аннотацией
@elephizeTarget
, она интерпретируется как функциональный React-компонент. - Для функционального компонента порождается отдельный класс, в который уходит вся логика, описанная внутри функции.
- Если функциональному компоненту требуются какие-то данные из внешней области (пример: defaultProps), они получаются из исходного компонента модуля через доступ к его полям.
- Каждый объект компонента на сервере является синглтоном. Таким образом можно получить объект компонента через статический метод
getInstance()
, если знать его класс и подключить файл с ним. - У каждого компонента декларируется публичный метод
render(array $props, array $children)
. Его можно использовать для подключения изоморфных компонентов во внешнем коде, передавая в качестве$children
пустой массив, а в качестве$props
- массив с данными, необходимыми для рендеринга компонента. Написание отдельного серверного кода для получения$props
, так же как и контроль над соответствием форматов данных, ложится на программиста.
В случае, если из изоморфного CommonJS-модуля импортируется некий файл, который не предполагается транспилировать, импорт следует занести в секцию ignoreImports
(см. также инструкцию по конфигурации). Это однозначно необходимо для функций, которые замещаются кастомными серверными реализациями при помощи опции customGlobals
. Необходимо помнить, что все не помеченные такой директивой импорты будут в обязательном порядке пройдены парсером и анализатором typescript, и в случае если они не предназначены для транспиляции - на выходе будет очень много ошибок.