Plugin patterns
Plugin.fs is one recursive module, AST. Its active patterns recognise the pieces of a tag: its constructor, its attributes and its children. Order matters in several places, and the comments say where.
Attributes and properties
The AttributesAndProperties module deals with everything that ends up as an attribute.
PropertyGetterrecognises a read likeprops.labeland returns the property name.PropertySetterrecognisesprops.label <- value, in all the shapes Fable produces for it: a setter call, amember valfield set, or a[<DefaultValue>] val mutablefield set.PropsGetterOrSettercombines the two. A getter is recorded withaddGetterand rewritten to a plain property read on the self identifier. A setter is recorded withaddSetterand removed from the body; it comes back as themerge.
AttributeExpressionrecognises one attribute and returns its name and value. It handles the special cases:.style',.class'andref.data,.attrand.bool, which builddata-*and other attribute names from a string.spread, written as{...PARTAS_OTHERS} n$when you spread the component's own props, or{...x} n$for any other identifier or field.n$={false}is the marker that closes a spread.- polymorphic attributes (
as',asChild, or any alias of__PARTAS_POLYMORPHIC__), whose value becomes a function ofPARTAS_POLYPROPSthat spreads them onto the tag ariasetters, renamed toaria-*
ValueUnrollerandMatchValueReplacerflatten thedelay/toArraywrappers Fable puts around list and array values in attributes. They only unrolltoArray/toListover a delayed sequence;List.toArray xsis a real conversion and stays.PropCollectorruns over a list of expressions and collects every attribute. Anything it does not recognise is dropped, withdebugDisposal.
Tags
TagConstructor recognises the expression that creates a tag, and returns an ElementBuilder. It tries, in order:
- constructors of types with
[<PartasImport>]or[<PartasProxyImport>], which need an import injected. These must come before the local tag case, or they are never reached. - a tag declared in this project (a Partas type, matched through its
_$ctor) - a native element
- a library import, and then a user-defined import
- a tag with properties, which Fable writes as a
letof the element followed by setters - a tag with an extension call such as
.spreador.data - a tag with children: a call to the builder's
Runwith the tag as its first argument - a context provider, imported or local, which becomes
<Context value={...}>(in Solid 2 the context is its own provider)
Children
BuilderCollector walks the expressions inside a tag's computation expression and returns the children. It is the most recursive part of the plugin. It:
- unwraps the builder's
letand lambda bindings (PARTAS_ELEMENT,PARTAS_BUILDER,PARTAS_YIELDand the rest) - turns the lambdas of
ChildLambdaProvidertypes, such as the child function ofFor.Keyed, into JSX functions with the right number of parameters - transforms conditionals, and lifts property getters and setters
- keeps field and tuple accessors whole, so a child like
item.Titleis not thrown away
collectTagInfo does the final clean-up of an ElementBuilder, trimming reserved suffixes off the attribute names.
Other patterns
SpecialAttributeTransformation.Pojorewrites a[<Pojo>]constructor and its setters into one object literal.SkipPojoOptimisationturns it off.TagValue.TagValueandTagValue.TagRenderhandle tag values: a tag stored as a value with!@, and rendered later with.render.Polymorphism.PolymorphicAttributedecides whether an attribute name is polymorphic.
See Transformation for how these are put together.
