Plugin spec
Spec.fs says what input the plugin accepts, holds the expressions the plugin builds over and over, and holds the patterns that recognise particular AST nodes. The patterns are grouped in modules named after what they match: MemberRef, Expr, Type, EntityRef, Ident and CallInfo.
FableRequirements
The plugin declares Fable 5.0 as its minimum version. Fable 4 does not detect it. The repository pins Fable 5.13.0 in .config/dotnet-tools.json, and that is the version the tests run on. The Language and FileExtension patterns check that the target is JavaScript and the extension ends in .js or .jsx.
SchemaRules
ValidMemberRef decides whether a member can take [<SolidTypeComponent>]. It matches when:
- the declaring type's full name starts with
Partas.Solid - the member is an instance member
- it takes only unit, so its arguments are the self identifier and a unit
On a match it returns the declaring type's DisplayName, which becomes the component's name, and the name of the self identifier. The identifier can have any name: props, this, someProps. On a mismatch it logs a warning and the attribute falls back to the plain transformation.
Baked
Prebuilt expressions that are verbose or used in several places.
convertGettersToObjectbindsPARTAS_OTHERS. With keys, it isomit(props, "a", "b"). With no keys, it is the props themselves.convertSettersToObjectprependsprops = merge({ ... }, props)when there are defaults. Keys have their reserved suffixes trimmed.renderElementturns anElementBuilderinto a JSX element. It also does one last rewrite: a tag namedFragmentloses its name, so it is written as<>...</>.untrackedwraps an expression inuntrack(() => expr), withuntrackimported fromsolid-js.untrackedCallwrites a direct call to a[<SolidComponent>]let binding asuntrack(Label.bind(undefined, ...args))(untrack(Label)without arguments): the body runs untracked, the arguments are evaluated in the caller's scope.
omit and merge are imported from solid-js; they replaced splitProps and mergeProps in Solid 2. Here they are together, adapted from the CombinedSpread snapshot test. The self identifier is this, which Fable writes as this$, and the plugin uses that name throughout:
[<Erase>]
type SimpleCombination() =
inherit div()
[<SolidTypeComponent>]
member this.View =
this.class' <- "ClassDefault"
div () {
span (class' = this.class') { "Reads class. " }
div().spread this { "Gets every prop but class: hover me." }
}
[<SolidComponent>]
let CombinationDemo () =
SimpleCombination(title = "title was spread")
export function SimpleCombination(this$) {
this$ = merge({
class: "ClassDefault",
}, this$);
const PARTAS_OTHERS = omit(this$, "class");
return <div>
<span class={this$.class}>
{"Reads class. "}
</span>
<div {...PARTAS_OTHERS} n$={false}>
Gets every prop but class: hover me.
</div>
</div>;
}
export function CombinationDemo() {
return <SimpleCombination title="title was spread" />;
}
Recognisers
A few examples of what the pattern modules do:
MemberRef.PartasNamereturns a member's name from its compiled name, withget_,set_,_$ctorand the reserved suffixes cleaned off. It matches any non-generated member reference.MemberRef.MemberRefIsclassifies a member reference as aMemberRefType.Expr.ImportedConstructor,ImportedSetterandImportedGetterrecognise calls to members declared in other modules, which Fable represents as imports.Type.HasPartasImportandType.HasPartasProxyImportfind the attributes that tell the plugin to import a tag from a library.Type.HasPojofinds[<Pojo>].Ident.IdentIsclassifies an identifier as anIdentType, including the component's self identifier.CallInfo.PropertySetterandCallInfo.Pojorecognise property assignments and pojo constructors.
