babel-plugin-transform-metadata
Strict, optimized and smart reflection metadata generator for classes and functions from flowtype metadata.
- Supports arrows and function expressions
- Metadata provided for array and object-style arguments
- Generics and type arguments support
Examples
Interface as value
Flowtype and typescript reflection does not support type annotations as value keys, so we use some trick with typecast.
In:
// @flow {} {} const id = _: C
Out:
'use strict'; { if !instance instanceof Constructor throw "Cannot call a class as a function"; } var { ;}; var { ;}; MyClassdisplayName = 'MyClass';MyClass_r = 0 'C'; var id = 'C';
Mark function
In:
// @flow { { }}
Out:
"use strict"; { {}}fndisplayName = "fn";fn_r = 1 A;
Components metadata
In:
// @flow {}interface State s: A { return <div>AA</div>}
Out:
{} { return ;} ComponentDdisplayName = 'ComponentD';ComponentD_r1 = s: A;
Type parameters
In example below, ISource and IStatus to ids mappings are configured in .babelrc. Only first type parameter used.
In:
// @flowtype ResultOf<F> = _ResultOf<* F>type _ResultOf<V V> = V { { }}
Out:
'use strict'; { {}} fndisplayName = 'fn';fn_r3 = 'babel-plugin-transform-metadata/src/__tests__/data/AllFeatures.js';fn_r2 = 2;fn_r1 = A B factory _r4: 1 v: A _r4: 2 v: A B;
For more examples see ./src/__tests__/data
Metadata
To each function plugin adds following metadata:
/** * Class constructor or function argument */ type IArg = IFunction | /** * User defined interface id, see markGenerics */ _r4: number; /** * Type arguments */ v: IFunction; /** * Each function or class constructor */ interface IFunction ...args: any: any; /** * constructor/function arguments list */ _r1?: IArg; /** * bit flags: 1 - jsx, 2 - fn */ _r2?: number; /** * relative filePath for hmr and debugging */ _r3?: string;
.babelrc options
Add before babel-plugin-transform-decorators-legacy and other transformation plugins.
interface IOptions /** * if true - add metadata only to exported function/classes */ onlyExports?: boolean; /** * if true - add file path to each exported class or function for hot reloading */ addFileName?: boolean; /** * if true - add function/class name to each exported class or function */ addDisplayName?: boolean; /** * how to generate interface name tokens: * fullPath - type name + crc(file with type path), typeName - type name only */ typeNameStrategy?: 'typeName' | 'fullPath'; /** * createElement/createVNode factory name, used for [reactive-di](https://github.com/zerkalica/reactive-di) components */ jsxPragma?: string; /** * Interface to ids mappings: {'ISource': 1, 'IStatus': 2} */ markGenerics?: id: string: number;
Example .babelrc:
Restrictions
For interface-based metadata we need to convert types to unique string tokens, something like this:
{}Reflection
JS module import subsystem is poor and nothing is doing in ES standarts for improving it. It's no clean way to identify imported interface in babel plugin, if import path is relative:
Types are same, but import paths is different:
Ideally, set "typeNameStrategy": "fullPath" and always use absolute path for types via name_mapper in .flowconfig:
module.name_mapper='^babel-plugin-transform-metadata/i/\(.*\)' -> '<PROJECT_ROOT>/i/\1'
{}Reflection// where crc1 is crc32('babel-plugin-transform-metadata/i/types')// where crc2 is crc32('internalTypes')
Relative paths supported, but some collisions possible, if types with equal names are defined in different files with equal names:
If "typeNameStrategy" is "fullPath", types always will be placed in separate files to avoid collisions like this:
// t1.js {}Reflection
// t2.js {}Reflection
If "typeNameStrategy" is "typeName", import paths will be ignored. But possible collisions with equal type names in different files.