github.com/octohelm/cuemod@v0.9.4/pkg/cuex/bundle.go (about) 1 package cuex 2 3 import ( 4 "fmt" 5 "regexp" 6 "sort" 7 "strconv" 8 "strings" 9 10 cueast "cuelang.org/go/cue/ast" 11 "cuelang.org/go/cue/build" 12 "cuelang.org/go/cue/format" 13 "github.com/octohelm/cuemod/pkg/cuemod/builtin" 14 ) 15 16 // BundleToRaw bundle instance to single cue file 17 func BundleToRaw(inst *build.Instance) ([]byte, error) { 18 sf := &bundler{ 19 stds: map[string]*cueast.ImportSpec{}, 20 imports: map[string]*cueast.Field{}, 21 } 22 23 f, err := sf.Export(inst) 24 if err != nil { 25 return nil, err 26 } 27 28 return format.Node(f, format.Simplify()) 29 } 30 31 type bundler struct { 32 stds map[string]*cueast.ImportSpec 33 34 imports map[string]*cueast.Field 35 importOrders []string 36 } 37 38 func (sf *bundler) importPkg(importPath string, f *cueast.Field) { 39 if _, ok := sf.imports[importPath]; !ok { 40 sf.imports[importPath] = f 41 42 cueast.AddComment(f, &cueast.CommentGroup{ 43 Doc: true, 44 List: []*cueast.Comment{{ 45 Text: "// " + importPath, 46 }}, 47 }) 48 49 sf.importOrders = append([]string{importPath}, sf.importOrders...) 50 } 51 } 52 53 func (sf *bundler) importDecl() *cueast.ImportDecl { 54 stds := make([]string, 0) 55 56 for i := range sf.stds { 57 stds = append(stds, i) 58 } 59 60 if len(stds) == 0 { 61 return nil 62 } 63 64 sort.Strings(stds) 65 66 d := &cueast.ImportDecl{} 67 68 d.Specs = make([]*cueast.ImportSpec, len(stds)) 69 70 for i, importPath := range stds { 71 d.Specs[i] = sf.stds[importPath] 72 } 73 74 return d 75 } 76 77 func (sf *bundler) importAliases() []cueast.Decl { 78 decls := make([]cueast.Decl, 0) 79 80 for _, importPath := range sf.importOrders { 81 decls = append(decls, sf.imports[importPath]) 82 } 83 84 return decls 85 } 86 87 func (sf *bundler) Export(inst *build.Instance) (*cueast.File, error) { 88 f, err := sf.Walk(inst) 89 if err != nil { 90 return nil, err 91 } 92 93 decls := f.Decls 94 95 f.Decls = make([]cueast.Decl, 0) 96 97 if importDecl := sf.importDecl(); importDecl != nil { 98 f.Decls = append(f.Decls, importDecl) 99 } 100 101 f.Decls = append(f.Decls, &cueast.StructLit{ 102 Elts: decls, 103 }) 104 105 f.Decls = append(f.Decls, sf.importAliases()...) 106 107 return f, nil 108 } 109 110 func (sf *bundler) Walk(inst *build.Instance) (*cueast.File, error) { 111 f := &cueast.File{ 112 Filename: fmt.Sprintf("%s/%s.cue", inst.ImportPath, inst.PkgName), 113 } 114 115 stmts := make([]cueast.Decl, 0) 116 importAliases := map[string]string{} 117 118 for _, file := range inst.Files { 119 for _, d := range file.Decls { 120 switch decl := d.(type) { 121 case *cueast.Package: 122 continue 123 case *cueast.ImportDecl: 124 for i := range decl.Specs { 125 spec := decl.Specs[i] 126 127 importPath, _ := strconv.Unquote(spec.Path.Value) 128 129 if builtin.IsBuiltIn(importPath) { 130 id := spec.Name 131 if spec.Name != nil { 132 id = cueast.NewIdent(spec.Name.Name) 133 } 134 sf.stds[importPath] = cueast.NewImport(id, importPath) 135 } else { 136 for _, dep := range inst.Imports { 137 if dep.ImportPath == importPath { 138 f, err := sf.Walk(dep) 139 if err != nil { 140 return nil, err 141 } 142 143 id := cueast.NewIdent(toSafeID(importPath)) 144 145 sf.importPkg(importPath, &cueast.Field{ 146 Label: id, 147 Value: &cueast.StructLit{ 148 Elts: f.Decls, 149 }, 150 }) 151 152 n := cueast.NewIdent(dep.PkgName) 153 154 if p := strings.Split(importPath, ":"); len(p) == 2 { 155 n = cueast.NewIdent(p[1]) 156 } 157 158 if spec.Name != nil { 159 n = cueast.NewIdent(spec.Name.Name) 160 } 161 162 importAliases[n.Name] = id.Name 163 } 164 } 165 } 166 } 167 168 default: 169 stmts = append(stmts, decl) 170 } 171 } 172 } 173 174 for _, stmt := range stmts { 175 cueast.Walk( 176 stmt, 177 func(node cueast.Node) bool { 178 if id, ok := node.(*cueast.Ident); ok && id.Node != nil { 179 if _, ok := id.Node.(*cueast.ImportSpec); ok { 180 for n, uniqPkgName := range importAliases { 181 if n == id.Name { 182 id.Name = uniqPkgName 183 } 184 } 185 } 186 } 187 return true 188 }, 189 nil, 190 ) 191 192 f.Decls = append(f.Decls, stmt) 193 } 194 195 return f, nil 196 } 197 198 var re = regexp.MustCompile(`[^0-9A-Za-z_]`) 199 200 func toSafeID(importPath string) string { 201 return "_" + re.ReplaceAllString(importPath, "_") 202 }