feat(codegen): order generated slots predictably
Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
f8558a52ef
commit
e4ee677bb7
2 changed files with 48 additions and 13 deletions
|
|
@ -33,6 +33,8 @@ var wcTemplate = template.Must(template.New("wc").Parse(`class {{.ClassName}} ex
|
|||
}`))
|
||||
|
||||
// GenerateClass produces a JS class definition for a custom element.
|
||||
//
|
||||
// cls, err := GenerateClass("nav-bar", "H")
|
||||
func GenerateClass(tag, slot string) (string, error) {
|
||||
if !strings.Contains(tag, "-") {
|
||||
return "", log.E("codegen.GenerateClass", "custom element tag must contain a hyphen: "+tag, nil)
|
||||
|
|
@ -52,11 +54,15 @@ func GenerateClass(tag, slot string) (string, error) {
|
|||
}
|
||||
|
||||
// GenerateRegistration produces the customElements.define() call.
|
||||
//
|
||||
// js := GenerateRegistration("nav-bar", "NavBar")
|
||||
func GenerateRegistration(tag, className string) string {
|
||||
return fmt.Sprintf(`customElements.define("%s", %s);`, tag, className)
|
||||
}
|
||||
|
||||
// TagToClassName converts a kebab-case tag to PascalCase class name.
|
||||
//
|
||||
// className := TagToClassName("nav-bar") // NavBar
|
||||
func TagToClassName(tag string) string {
|
||||
var b strings.Builder
|
||||
for p := range strings.SplitSeq(tag, "-") {
|
||||
|
|
@ -70,10 +76,12 @@ func TagToClassName(tag string) string {
|
|||
|
||||
// GenerateBundle produces all WC class definitions and registrations
|
||||
// for a set of HLCRF slot assignments.
|
||||
//
|
||||
// js, err := GenerateBundle(map[string]string{"H":"nav-bar", "C":"main-content"})
|
||||
func GenerateBundle(slots map[string]string) (string, error) {
|
||||
var b strings.Builder
|
||||
|
||||
for _, entry := range sortedSlotEntries(slots) {
|
||||
for _, entry := range orderedSlotEntries(slots) {
|
||||
cls, err := GenerateClass(entry.Tag, entry.Slot)
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
|
@ -88,8 +96,10 @@ func GenerateBundle(slots map[string]string) (string, error) {
|
|||
|
||||
// GenerateTypeDefinitions produces a TypeScript declaration file for the
|
||||
// generated custom elements.
|
||||
//
|
||||
// dts, err := GenerateTypeDefinitions(map[string]string{"H":"nav-bar"})
|
||||
func GenerateTypeDefinitions(slots map[string]string) (string, error) {
|
||||
entries := sortedSlotEntries(slots)
|
||||
entries := orderedSlotEntries(slots)
|
||||
var b strings.Builder
|
||||
|
||||
for _, entry := range entries {
|
||||
|
|
@ -124,18 +134,35 @@ type slotEntry struct {
|
|||
Tag string
|
||||
}
|
||||
|
||||
func sortedSlotEntries(slots map[string]string) []slotEntry {
|
||||
keys := slices.Collect(maps.Keys(slots))
|
||||
slices.Sort(keys)
|
||||
var canonicalSlotOrder = []string{"H", "L", "C", "R", "F"}
|
||||
|
||||
seen := make(map[string]bool, len(keys))
|
||||
entries := make([]slotEntry, 0, len(keys))
|
||||
for _, slot := range keys {
|
||||
tag := slots[slot]
|
||||
if seen[tag] {
|
||||
func orderedSlotEntries(slots map[string]string) []slotEntry {
|
||||
seenSlots := make(map[string]bool, len(slots))
|
||||
entries := make([]slotEntry, 0, len(slots))
|
||||
|
||||
for _, slot := range canonicalSlotOrder {
|
||||
tag, ok := slots[slot]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
seen[tag] = true
|
||||
if seenSlots[tag] {
|
||||
continue
|
||||
}
|
||||
seenSlots[tag] = true
|
||||
entries = append(entries, slotEntry{Slot: slot, Tag: tag})
|
||||
}
|
||||
|
||||
keys := slices.Collect(maps.Keys(slots))
|
||||
slices.Sort(keys)
|
||||
for _, slot := range keys {
|
||||
if slot == "H" || slot == "L" || slot == "C" || slot == "R" || slot == "F" {
|
||||
continue
|
||||
}
|
||||
tag := slots[slot]
|
||||
if seenSlots[tag] {
|
||||
continue
|
||||
}
|
||||
seenSlots[tag] = true
|
||||
entries = append(entries, slotEntry{Slot: slot, Tag: tag})
|
||||
}
|
||||
return entries
|
||||
|
|
|
|||
|
|
@ -43,14 +43,22 @@ func TestTagToClassName_Good(t *testing.T) {
|
|||
|
||||
func TestGenerateBundle_Good(t *testing.T) {
|
||||
slots := map[string]string{
|
||||
"H": "nav-bar",
|
||||
"C": "main-content",
|
||||
"H": "nav-bar",
|
||||
"F": "page-footer",
|
||||
}
|
||||
js, err := GenerateBundle(slots)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, js, "NavBar")
|
||||
assert.Contains(t, js, "MainContent")
|
||||
assert.Equal(t, 2, strings.Count(js, "extends HTMLElement"))
|
||||
assert.Contains(t, js, "PageFooter")
|
||||
assert.Equal(t, 3, strings.Count(js, "extends HTMLElement"))
|
||||
|
||||
h := strings.Index(js, "NavBar")
|
||||
c := strings.Index(js, "MainContent")
|
||||
f := strings.Index(js, "PageFooter")
|
||||
assert.True(t, h >= 0 && c >= 0 && f >= 0, "expected all generated classes in output")
|
||||
assert.True(t, h < c && c < f, "expected canonical HLCRF order in generated bundle")
|
||||
}
|
||||
|
||||
func TestGenerateTypeDefinitions_Good(t *testing.T) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue