diff --git a/codegen/codegen.go b/codegen/codegen.go index f679823..ca66162 100644 --- a/codegen/codegen.go +++ b/codegen/codegen.go @@ -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 diff --git a/codegen/codegen_test.go b/codegen/codegen_test.go index fadbc28..aa68512 100644 --- a/codegen/codegen_test.go +++ b/codegen/codegen_test.go @@ -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) {