Skip to content

Commit

Permalink
limit the public interface, document the public interface a bit better
Browse files Browse the repository at this point in the history
  • Loading branch information
torenware committed Mar 26, 2022
1 parent 3db7848 commit c798768
Show file tree
Hide file tree
Showing 2 changed files with 145 additions and 139 deletions.
138 changes: 138 additions & 0 deletions manifest-parser.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
package vueglue

import (
"encoding/json"
"fmt"
"reflect"
"strconv"
)

type manifestNode struct {
key string
nodeType reflect.Kind
value reflect.Value
children []*manifestNode
}

type manifestTarget struct {
File string `json:"file"`
Source string `json:"src"`
IsEntry bool `json:"isEntry"`
Imports []string `json:"imports"`
CSS []string `json:"css"`
Nodes []*manifestNode
}

func (n *manifestNode) subKey(key string) *manifestNode {
if len(n.children) == 0 {
return nil
}
for _, leaf := range n.children {
if leaf.key == key {
return leaf
}
}
return nil
}

// @see https://yourbasic.org/golang/json-example/
func (m *manifestTarget) parseWithoutReflection(jsonData []byte) (*VueGlue, error) {
var v interface{}
json.Unmarshal(jsonData, &v)
topNode := manifestNode{
key: "top",
}
m.Nodes = append(m.Nodes, &topNode)
m.siftCollections(&topNode, "", "", v)

// Get entry point
entry := (*manifestNode)(nil)
glue := &VueGlue{}

for _, leaf := range topNode.children {
if leaf.subKey("isEntry") != nil {
entry = leaf
glue.MainModule = leaf.subKey("file").value.String()
break
}
}
if entry == nil {
return nil, ErrNoEntryPoint
}

imports := entry.subKey("imports")
if imports == nil || len(imports.children) == 0 {
// return nil, errors.New("expected code to have js dependencies")
// turns out this will become optional as of Vite 2.9, so:
} else {
for _, child := range imports.children {
// these have a level of indirection for some reason
deref := topNode.subKey(child.value.String())
if deref == nil {
return nil, ErrNoInputFile
}
item := deref.subKey("file")
if item == nil {
return nil, ErrManifestBadlyFormed
}
glue.Imports = append(glue.Imports, item.value.String())
}
}

css := entry.subKey("css")
if css == nil || len(css.children) == 0 {
// not an error, since CSS is optional
return glue, nil
}

for _, child := range css.children {
glue.CSSModule = append(glue.CSSModule, child.value.String())
}

return glue, nil
}

func (m *manifestTarget) siftCollections(leaf *manifestNode, indent, key string, v interface{}) {
data, ok := v.(map[string]interface{})
if ok {
leaf.nodeType = reflect.Map
for k, v := range data {
child := &manifestNode{
key: k,
}
leaf.children = append(leaf.children, child)
m.processInterface(child, indent, k, v)
}
} else if arrayData, ok := v.([]interface{}); ok {
leaf.nodeType = reflect.Slice
for i, v := range arrayData {
child := &manifestNode{}
leaf.children = append(leaf.children, child)
m.processInterface(child, indent, strconv.Itoa(i), v)
}
} else {
m.processInterface(leaf, indent, key, v)
}
}

// call this for recurisve structures.
func (m *manifestTarget) processInterface(leaf *manifestNode, indent, k string, v interface{}) {
// Cover types we know we get in JSON
switch v := v.(type) {
case string:
leaf.nodeType = reflect.String
leaf.value = reflect.ValueOf(v)
case float64:
leaf.nodeType = reflect.Float64
leaf.value = reflect.ValueOf(v)
case bool:
leaf.nodeType = reflect.Bool
leaf.value = reflect.ValueOf(v)
case []interface{}:
m.siftCollections(leaf, indent+" ", k, v)
case map[string]interface{}:
m.siftCollections(leaf, indent+" ", k, v)
default:
fmt.Printf("%s %s ?? %T (unknown)", indent, k, v)
}
}
146 changes: 7 additions & 139 deletions vueglue.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,9 @@ import (
"fmt"
"io/fs"
"path/filepath"
"reflect"
"strconv"
)

const (
AssetsDir = "dist/assets"
RootItem = "src/main.ts" // set in vite.config.ts
)

// type VueGlue summarizes a manifest file, and points to the assets.
type VueGlue struct {

// Entry point for JS
Expand All @@ -26,153 +20,27 @@ type VueGlue struct {
CSSModule []string

// I use a 'data-entryp' attribute to find what
// components to load. Lookup is in src/main.ts.
// components to load. Lookup is in the entry point JS.
// This makes the info easily available in templates.
MountPoint string

// An embed that points to the Vue/Vite dist
// directory.
DistFS fs.ReadFileFS
}

type ManifestNode struct {
Key string
Type reflect.Kind
Value reflect.Value
Children []*ManifestNode
}

type ManifestTarget struct {
File string `json:"file"`
Source string `json:"src"`
IsEntry bool `json:"isEntry"`
Imports []string `json:"imports"`
CSS []string `json:"css"`
Nodes []*ManifestNode
}

func (n *ManifestNode) subKey(key string) *ManifestNode {
if len(n.Children) == 0 {
return nil
}
for _, leaf := range n.Children {
if leaf.Key == key {
return leaf
}
}
return nil
}

// @see https://yourbasic.org/golang/json-example/
func (m *ManifestTarget) parseWithoutReflection(jsonData []byte) (*VueGlue, error) {
var v interface{}
json.Unmarshal(jsonData, &v)
topNode := ManifestNode{
Key: "top",
}
m.Nodes = append(m.Nodes, &topNode)
m.siftCollections(&topNode, "", "", v)

// Get entry point
entry := (*ManifestNode)(nil)
glue := &VueGlue{}

for _, leaf := range topNode.Children {
if leaf.subKey("isEntry") != nil {
entry = leaf
glue.MainModule = leaf.subKey("file").Value.String()
break
}
}
if entry == nil {
return nil, ErrNoEntryPoint
}

imports := entry.subKey("imports")
if imports == nil || len(imports.Children) == 0 {
// return nil, errors.New("expected code to have js dependencies")
// turns out this will become optional as of Vite 2.9, so:
} else {
for _, child := range imports.Children {
// these have a level of indirection for some reason
deref := topNode.subKey(child.Value.String())
if deref == nil {
return nil, ErrNoInputFile
}
item := deref.subKey("file")
if item == nil {
return nil, ErrManifestBadlyFormed
}
glue.Imports = append(glue.Imports, item.Value.String())
}
}

css := entry.subKey("css")
if css == nil || len(css.Children) == 0 {
// not an error, since CSS is optional
return glue, nil
}

for _, child := range css.Children {
glue.CSSModule = append(glue.CSSModule, child.Value.String())
}

return glue, nil
}

func (m *ManifestTarget) siftCollections(leaf *ManifestNode, indent, key string, v interface{}) {
data, ok := v.(map[string]interface{})
if ok {
leaf.Type = reflect.Map
for k, v := range data {
child := &ManifestNode{
Key: k,
}
leaf.Children = append(leaf.Children, child)
m.processInterface(child, indent, k, v)
}
} else if arrayData, ok := v.([]interface{}); ok {
leaf.Type = reflect.Slice
for i, v := range arrayData {
child := &ManifestNode{}
leaf.Children = append(leaf.Children, child)
m.processInterface(child, indent, strconv.Itoa(i), v)
}
} else {
m.processInterface(leaf, indent, key, v)
}
}

// call this for recurisve structures.
func (m *ManifestTarget) processInterface(leaf *ManifestNode, indent, k string, v interface{}) {
// Cover types we know we get in JSON
switch v := v.(type) {
case string:
leaf.Type = reflect.String
leaf.Value = reflect.ValueOf(v)
case float64:
leaf.Type = reflect.Float64
leaf.Value = reflect.ValueOf(v)
case bool:
leaf.Type = reflect.Bool
leaf.Value = reflect.ValueOf(v)
case []interface{}:
m.siftCollections(leaf, indent+" ", k, v)
case map[string]interface{}:
m.siftCollections(leaf, indent+" ", k, v)
default:
fmt.Printf("%s %s ?? %T (unknown)", indent, k, v)
}
}

// ParseManifest imports and parses a manifest returning a glue object.
func ParseManifest(contents []byte) (*VueGlue, error) {
var testRslt ManifestTarget
var testRslt manifestTarget
glue, err := testRslt.parseWithoutReflection(contents)
if err != nil {
return nil, err
}
return glue, nil
}

// NewVueGlue finds the manifest in the supplied file system
// and returns a glue object.
func NewVueGlue(dist fs.ReadFileFS, pathToDist string) (*VueGlue, error) {

if !fs.ValidPath(pathToDist) {
Expand Down

0 comments on commit c798768

Please sign in to comment.