| 
									
										
										
										
											2022-05-27 09:21:37 +08:00
										 |  |  | package codegen | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"bytes" | 
					
						
							|  |  |  | 	gerrors "errors" | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"io" | 
					
						
							|  |  |  | 	"io/fs" | 
					
						
							|  |  |  | 	"path/filepath" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							|  |  |  | 	"text/template" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"cuelang.org/go/cue" | 
					
						
							|  |  |  | 	"cuelang.org/go/cue/ast" | 
					
						
							| 
									
										
										
										
											2022-06-07 08:52:44 +08:00
										 |  |  | 	"cuelang.org/go/cue/build" | 
					
						
							|  |  |  | 	"cuelang.org/go/cue/cuecontext" | 
					
						
							| 
									
										
										
										
											2022-05-27 09:21:37 +08:00
										 |  |  | 	"cuelang.org/go/cue/errors" | 
					
						
							| 
									
										
										
										
											2022-06-07 08:52:44 +08:00
										 |  |  | 	"cuelang.org/go/cue/load" | 
					
						
							| 
									
										
										
										
											2022-05-27 09:21:37 +08:00
										 |  |  | 	"cuelang.org/go/cue/parser" | 
					
						
							|  |  |  | 	"github.com/grafana/cuetsy" | 
					
						
							| 
									
										
										
										
											2022-06-07 08:52:44 +08:00
										 |  |  | 	"github.com/grafana/grafana" | 
					
						
							|  |  |  | 	"github.com/grafana/thema" | 
					
						
							|  |  |  | 	tload "github.com/grafana/thema/load" | 
					
						
							| 
									
										
										
										
											2022-05-27 09:21:37 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // The only import statement we currently allow in any models.cue file
 | 
					
						
							| 
									
										
										
										
											2022-06-07 08:52:44 +08:00
										 |  |  | const schemasPath = "github.com/grafana/grafana/packages/grafana-schema/src/schema" | 
					
						
							| 
									
										
										
										
											2022-05-27 09:21:37 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-07 08:52:44 +08:00
										 |  |  | // CUE import paths, mapped to corresponding TS import paths. An empty value
 | 
					
						
							|  |  |  | // indicates the import path should be dropped in the conversion to TS. Imports
 | 
					
						
							|  |  |  | // not present in the list are not not allowed, and code generation will fail.
 | 
					
						
							| 
									
										
										
										
											2022-05-27 09:21:37 +08:00
										 |  |  | var importMap = map[string]string{ | 
					
						
							| 
									
										
										
										
											2022-06-07 08:52:44 +08:00
										 |  |  | 	"github.com/grafana/thema": "", | 
					
						
							|  |  |  | 	schemasPath:                "@grafana/schema", | 
					
						
							| 
									
										
										
										
											2022-05-27 09:21:37 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Hard-coded list of paths to skip. Remove a particular file as we're ready
 | 
					
						
							|  |  |  | // to rely on the TypeScript auto-generated by cuetsy for that particular file.
 | 
					
						
							|  |  |  | var skipPaths = []string{ | 
					
						
							|  |  |  | 	"public/app/plugins/panel/canvas/models.cue", | 
					
						
							| 
									
										
										
										
											2022-06-24 02:48:28 +08:00
										 |  |  | 	"public/app/plugins/panel/heatmap/models.cue", | 
					
						
							|  |  |  | 	"public/app/plugins/panel/heatmap-old/models.cue", | 
					
						
							| 
									
										
										
										
											2022-05-27 09:21:37 +08:00
										 |  |  | 	"public/app/plugins/panel/candlestick/models.cue", | 
					
						
							|  |  |  | 	"public/app/plugins/panel/state-timeline/models.cue", | 
					
						
							|  |  |  | 	"public/app/plugins/panel/status-history/models.cue", | 
					
						
							|  |  |  | 	"public/app/plugins/panel/table/models.cue", | 
					
						
							|  |  |  | 	"public/app/plugins/panel/timeseries/models.cue", | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const prefix = "/" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // CuetsifyPlugins runs cuetsy against plugins' models.cue files.
 | 
					
						
							|  |  |  | func CuetsifyPlugins(ctx *cue.Context, root string) (WriteDiffer, error) { | 
					
						
							| 
									
										
										
										
											2022-06-07 08:52:44 +08:00
										 |  |  | 	lib := thema.NewLibrary(ctx) | 
					
						
							| 
									
										
										
										
											2022-05-27 09:21:37 +08:00
										 |  |  | 	// TODO this whole func has a lot of old, crufty behavior from the scuemata era; needs TLC
 | 
					
						
							| 
									
										
										
										
											2022-06-07 08:52:44 +08:00
										 |  |  | 	overlay := make(map[string]load.Source) | 
					
						
							|  |  |  | 	err := toOverlay(prefix, grafana.CueSchemaFS, overlay) | 
					
						
							|  |  |  | 	// err := tload.ToOverlay(prefix, grafana.CueSchemaFS, overlay)
 | 
					
						
							| 
									
										
										
										
											2022-05-27 09:21:37 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	exclude := func(path string) bool { | 
					
						
							|  |  |  | 		for _, p := range skipPaths { | 
					
						
							|  |  |  | 			if path == p { | 
					
						
							|  |  |  | 				return true | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-07 08:52:44 +08:00
										 |  |  | 		return filepath.Dir(path) == "cue.mod" | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Prep the cue load config
 | 
					
						
							|  |  |  | 	clcfg := &load.Config{ | 
					
						
							|  |  |  | 		Overlay: overlay, | 
					
						
							|  |  |  | 		// FIXME these module paths won't work for things not under our cue.mod - AKA third-party plugins
 | 
					
						
							|  |  |  | 		ModuleRoot: prefix, | 
					
						
							|  |  |  | 		Module:     "github.com/grafana/grafana", | 
					
						
							| 
									
										
										
										
											2022-05-27 09:21:37 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	outfiles := NewWriteDiffer() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	cuetsify := func(in fs.FS) error { | 
					
						
							|  |  |  | 		seen := make(map[string]bool) | 
					
						
							|  |  |  | 		return fs.WalkDir(in, ".", func(path string, d fs.DirEntry, err error) error { | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return err | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			dir := filepath.Dir(path) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if d.IsDir() || filepath.Ext(d.Name()) != ".cue" || seen[dir] || exclude(path) { | 
					
						
							|  |  |  | 				return nil | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			seen[dir] = true | 
					
						
							|  |  |  | 			clcfg.Dir = filepath.Join(root, dir) | 
					
						
							| 
									
										
										
										
											2022-06-07 08:52:44 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			var b []byte | 
					
						
							|  |  |  | 			f := &tsFile{} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			switch { | 
					
						
							|  |  |  | 			default: | 
					
						
							|  |  |  | 				insts := load.Instances(nil, clcfg) | 
					
						
							|  |  |  | 				if len(insts) > 1 { | 
					
						
							|  |  |  | 					return fmt.Errorf("%s: resulted in more than one instance", path) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				v := ctx.BuildInstance(insts[0]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				b, err = cuetsy.Generate(v, cuetsy.Config{}) | 
					
						
							| 
									
										
										
										
											2022-05-27 09:21:37 +08:00
										 |  |  | 				if err != nil { | 
					
						
							| 
									
										
										
										
											2022-06-07 08:52:44 +08:00
										 |  |  | 					return err | 
					
						
							| 
									
										
										
										
											2022-05-27 09:21:37 +08:00
										 |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-07 08:52:44 +08:00
										 |  |  | 			case strings.Contains(path, "public/app/plugins"): // panel plugin models.cue files
 | 
					
						
							|  |  |  | 				// The simple - and preferable - thing would be to have plugins use the same
 | 
					
						
							|  |  |  | 				// package name for their models.cue as their containing dir. That's not
 | 
					
						
							|  |  |  | 				// possible, though, because we allow dashes in plugin names, but CUE does not
 | 
					
						
							|  |  |  | 				// allow them in package names. Yuck.
 | 
					
						
							|  |  |  | 				inst, err := loadInstancesWithThema(in, dir, "grafanaschema") | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							|  |  |  | 					return fmt.Errorf("could not load CUE instance for %s: %w", dir, err) | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2022-05-27 09:21:37 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-07 08:52:44 +08:00
										 |  |  | 				// Also parse file directly to extract imports.
 | 
					
						
							|  |  |  | 				// NOTE this will need refactoring to support working with more than one file at a time
 | 
					
						
							|  |  |  | 				of, _ := in.Open(path) | 
					
						
							|  |  |  | 				pf, _ := parser.ParseFile(filepath.Base(path), of, parser.ParseComments) | 
					
						
							| 
									
										
										
										
											2022-05-27 09:21:37 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-07 08:52:44 +08:00
										 |  |  | 				iseen := make(map[string]bool) | 
					
						
							|  |  |  | 				for _, im := range pf.Imports { | 
					
						
							| 
									
										
										
										
											2022-05-27 09:21:37 +08:00
										 |  |  | 					ip := strings.Trim(im.Path.Value, "\"") | 
					
						
							| 
									
										
										
										
											2022-06-07 08:52:44 +08:00
										 |  |  | 					mappath, has := importMap[ip] | 
					
						
							|  |  |  | 					if !has { | 
					
						
							| 
									
										
										
										
											2022-05-27 09:21:37 +08:00
										 |  |  | 						// TODO make a specific error type for this
 | 
					
						
							| 
									
										
										
										
											2022-06-07 08:52:44 +08:00
										 |  |  | 						var all []string | 
					
						
							|  |  |  | 						for im := range importMap { | 
					
						
							|  |  |  | 							all = append(all, fmt.Sprintf("\t%s", im)) | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 						return errors.Newf(im.Pos(), "%s: import %q not allowed, panel plugins may only import from:\n%s\n", path, ip, strings.Join(all, "\n")) | 
					
						
							| 
									
										
										
										
											2022-05-27 09:21:37 +08:00
										 |  |  | 					} | 
					
						
							|  |  |  | 					// TODO this approach will silently swallow the unfixable
 | 
					
						
							|  |  |  | 					// error case where multiple files in the same dir import
 | 
					
						
							|  |  |  | 					// the same package to a different ident
 | 
					
						
							| 
									
										
										
										
											2022-06-07 08:52:44 +08:00
										 |  |  | 					if mappath != "" && !iseen[ip] { | 
					
						
							|  |  |  | 						iseen[ip] = true | 
					
						
							| 
									
										
										
										
											2022-05-27 09:21:37 +08:00
										 |  |  | 						f.Imports = append(f.Imports, convertImport(im)) | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-07 08:52:44 +08:00
										 |  |  | 				v := ctx.BuildInstance(inst) | 
					
						
							| 
									
										
										
										
											2022-05-27 09:21:37 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-07 08:52:44 +08:00
										 |  |  | 				lin, err := thema.BindLineage(v.LookupPath(cue.ParsePath("Panel")), lib) | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							|  |  |  | 					return fmt.Errorf("%s: failed to bind lineage: %w", path, err) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				f.V = thema.LatestVersion(lin) | 
					
						
							|  |  |  | 				f.WriteModelVersion = true | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				b, err = cuetsy.Generate(thema.SchemaP(lin, f.V).UnwrapCUE(), cuetsy.Config{}) | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							|  |  |  | 					return err | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2022-05-27 09:21:37 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2022-06-07 08:52:44 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-27 09:21:37 +08:00
										 |  |  | 			f.Body = string(b) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			var buf bytes.Buffer | 
					
						
							|  |  |  | 			err = tsTemplate.Execute(&buf, f) | 
					
						
							|  |  |  | 			outfiles[filepath.Join(root, strings.Replace(path, ".cue", ".gen.ts", -1))] = buf.Bytes() | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-07 08:52:44 +08:00
										 |  |  | 	err = cuetsify(grafana.CueSchemaFS) | 
					
						
							| 
									
										
										
										
											2022-05-27 09:21:37 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, gerrors.New(errors.Details(err, nil)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return outfiles, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func convertImport(im *ast.ImportSpec) *tsImport { | 
					
						
							|  |  |  | 	tsim := &tsImport{ | 
					
						
							| 
									
										
										
										
											2022-06-07 08:52:44 +08:00
										 |  |  | 		Pkg: importMap[schemasPath], | 
					
						
							| 
									
										
										
										
											2022-05-27 09:21:37 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	if im.Name != nil && im.Name.String() != "" { | 
					
						
							|  |  |  | 		tsim.Ident = im.Name.String() | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		sl := strings.Split(im.Path.Value, "/") | 
					
						
							|  |  |  | 		final := sl[len(sl)-1] | 
					
						
							|  |  |  | 		if idx := strings.Index(final, ":"); idx != -1 { | 
					
						
							|  |  |  | 			tsim.Pkg = final[idx:] | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			tsim.Pkg = final | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return tsim | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-07 08:52:44 +08:00
										 |  |  | var themamodpath string = filepath.Join("cue.mod", "pkg", "github.com", "grafana", "thema") | 
					
						
							| 
									
										
										
										
											2022-05-27 09:21:37 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-07 08:52:44 +08:00
										 |  |  | // all copied and hacked up from Thema's LoadInstancesWithThema, simply to allow setting the
 | 
					
						
							|  |  |  | // package name
 | 
					
						
							|  |  |  | func loadInstancesWithThema(modFS fs.FS, dir string, pkgname string) (*build.Instance, error) { | 
					
						
							|  |  |  | 	var modname string | 
					
						
							|  |  |  | 	err := fs.WalkDir(modFS, "cue.mod", func(path string, d fs.DirEntry, err error) error { | 
					
						
							|  |  |  | 		// fs.FS implementations tend to not use path separators as expected. Use a
 | 
					
						
							|  |  |  | 		// normalized one for comparisons, but retain the original for calls back into modFS.
 | 
					
						
							|  |  |  | 		normpath := filepath.FromSlash(path) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if d.IsDir() { | 
					
						
							|  |  |  | 			switch normpath { | 
					
						
							|  |  |  | 			case filepath.Join("cue.mod", "gen"), filepath.Join("cue.mod", "usr"): | 
					
						
							|  |  |  | 				return fs.SkipDir | 
					
						
							|  |  |  | 			case themamodpath: | 
					
						
							|  |  |  | 				return fmt.Errorf("path %q already exists in modFS passed to InstancesWithThema, must be absent for dynamic dependency injection", themamodpath) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			return nil | 
					
						
							|  |  |  | 		} else if normpath == filepath.Join("cue.mod", "module.cue") { | 
					
						
							|  |  |  | 			modf, err := modFS.Open(path) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return err | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			defer modf.Close() // nolint: errcheck
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			b, err := io.ReadAll(modf) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return err | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			modname, err = cuecontext.New().CompileBytes(b).LookupPath(cue.MakePath(cue.Str("module"))).String() | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return err | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if modname == "" { | 
					
						
							|  |  |  | 				return fmt.Errorf("InstancesWithThema requires non-empty module name in modFS' cue.mod/module.cue") | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2022-05-27 09:21:37 +08:00
										 |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-07 08:52:44 +08:00
										 |  |  | 	if modname == "" { | 
					
						
							|  |  |  | 		return nil, errors.New("cue.mod/module.cue did not exist") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	modroot := filepath.FromSlash(filepath.Join("/", modname)) | 
					
						
							|  |  |  | 	overlay := make(map[string]load.Source) | 
					
						
							|  |  |  | 	if err := tload.ToOverlay(modroot, modFS, overlay); err != nil { | 
					
						
							| 
									
										
										
										
											2022-05-27 09:21:37 +08:00
										 |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-07 08:52:44 +08:00
										 |  |  | 	// Special case for when we're calling this loader with paths inside the thema module
 | 
					
						
							|  |  |  | 	if modname == "github.com/grafana/thema" { | 
					
						
							|  |  |  | 		if err := tload.ToOverlay(modroot, thema.CueJointFS, overlay); err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		if err := tload.ToOverlay(filepath.Join(modroot, themamodpath), thema.CueFS, overlay); err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if dir == "" { | 
					
						
							|  |  |  | 		dir = "." | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	cfg := &load.Config{ | 
					
						
							|  |  |  | 		Overlay:    overlay, | 
					
						
							|  |  |  | 		ModuleRoot: modroot, | 
					
						
							|  |  |  | 		Module:     modname, | 
					
						
							|  |  |  | 		Dir:        filepath.Join(modroot, dir), | 
					
						
							|  |  |  | 		Package:    pkgname, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if dir == "." { | 
					
						
							|  |  |  | 		cfg.Package = filepath.Base(modroot) | 
					
						
							|  |  |  | 		cfg.Dir = modroot | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	inst := load.Instances(nil, cfg)[0] | 
					
						
							|  |  |  | 	if inst.Err != nil { | 
					
						
							|  |  |  | 		return nil, inst.Err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return inst, nil | 
					
						
							| 
									
										
										
										
											2022-05-27 09:21:37 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-07 08:52:44 +08:00
										 |  |  | func toOverlay(prefix string, vfs fs.FS, overlay map[string]load.Source) error { | 
					
						
							| 
									
										
										
										
											2022-05-27 09:21:37 +08:00
										 |  |  | 	if !filepath.IsAbs(prefix) { | 
					
						
							|  |  |  | 		return fmt.Errorf("must provide absolute path prefix when generating cue overlay, got %q", prefix) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	err := fs.WalkDir(vfs, ".", func(path string, d fs.DirEntry, err error) error { | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if d.IsDir() { | 
					
						
							|  |  |  | 			return nil | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		f, err := vfs.Open(path) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		defer func(f fs.File) { | 
					
						
							|  |  |  | 			err := f.Close() | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}(f) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		b, err := io.ReadAll(f) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-07 08:52:44 +08:00
										 |  |  | 		overlay[filepath.Join(prefix, path)] = load.FromBytes(b) | 
					
						
							| 
									
										
										
										
											2022-05-27 09:21:37 +08:00
										 |  |  | 		return nil | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type tsFile struct { | 
					
						
							| 
									
										
										
										
											2022-06-07 08:52:44 +08:00
										 |  |  | 	V                 thema.SyntacticVersion | 
					
						
							|  |  |  | 	WriteModelVersion bool | 
					
						
							|  |  |  | 	Imports           []*tsImport | 
					
						
							|  |  |  | 	Body              string | 
					
						
							| 
									
										
										
										
											2022-05-27 09:21:37 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type tsImport struct { | 
					
						
							|  |  |  | 	Ident string | 
					
						
							|  |  |  | 	Pkg   string | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var tsTemplate = template.Must(template.New("cuetsygen").Parse(`//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
					
						
							|  |  |  | // This file is autogenerated. DO NOT EDIT.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // To regenerate, run "make gen-cue" from the repository root.
 | 
					
						
							|  |  |  | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
					
						
							|  |  |  | {{range .Imports}} | 
					
						
							|  |  |  | import * as {{.Ident}} from '{{.Pkg}}';{{end}} | 
					
						
							| 
									
										
										
										
											2022-06-07 08:52:44 +08:00
										 |  |  | {{if .WriteModelVersion }} | 
					
						
							|  |  |  | export const modelVersion = Object.freeze([{{index .V 0}}, {{index .V 1}}]); | 
					
						
							| 
									
										
										
										
											2022-05-27 09:21:37 +08:00
										 |  |  | {{end}} | 
					
						
							|  |  |  | {{.Body}}`)) |