package init import ( "bytes" "context" _ "embed" "encoding/json" "fmt" "io" "os" "path/filepath" "github.com/go-errors/errors" "github.com/spf13/afero" "github.com/supabase/cli/internal/utils" "github.com/tidwall/jsonc" ) var ( vscodeDir = ".vscode" extensionsPath = filepath.Join(vscodeDir, "extensions.json") settingsPath = filepath.Join(vscodeDir, "settings.json") intellijDir = ".idea" denoPath = filepath.Join(intellijDir, "deno.xml") //go:embed templates/.gitignore initGitignore []byte //go:embed templates/.vscode/extensions.json vscodeExtensions string //go:embed templates/.vscode/settings.json vscodeSettings string //go:embed templates/.idea/deno.xml intelliJDeno string ) func Run(ctx context.Context, fsys afero.Fs, createVscodeSettings, createIntellijSettings *bool, params utils.InitParams) error { // 1. Write `config.toml`. if err := utils.InitConfig(params, fsys); err != nil { if errors.Is(err, os.ErrExist) { utils.CmdSuggestion = fmt.Sprintf("Run %s to overwrite existing config file.", utils.Aqua("supabase init --force")) } return err } // 2. Append to `.gitignore`. if utils.IsGitRepo() { if err := updateGitIgnore(utils.GitIgnorePath, fsys); err != nil { return err } } // 3. Generate VS Code settings. if createVscodeSettings != nil { if *createVscodeSettings { return writeVscodeConfig(fsys) } } else if createIntellijSettings != nil { if *createIntellijSettings { return writeIntelliJConfig(fsys) } } else { console := utils.NewConsole() if isVscode, err := console.PromptYesNo(ctx, "Generate VS Code settings for Deno?", false); err != nil { return err } else if isVscode { return writeVscodeConfig(fsys) } if isIntelliJ, err := console.PromptYesNo(ctx, "Generate IntelliJ Settings for Deno?", false); err != nil { return err } else if isIntelliJ { return writeIntelliJConfig(fsys) } } return nil } func updateGitIgnore(ignorePath string, fsys afero.Fs) error { var contents []byte if contained, err := afero.FileContainsBytes(fsys, ignorePath, initGitignore); contained { return nil } else if err == nil { // Add a line break when appending contents = append(contents, '\n') } else if !errors.Is(err, os.ErrNotExist) { return errors.Errorf("failed to read git ignore file: %w", err) } f, err := fsys.OpenFile(ignorePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if err != nil { return errors.Errorf("failed to create git ignore file: %w", err) } defer f.Close() if _, err := f.Write(append(contents, initGitignore...)); err != nil { return errors.Errorf("failed to write git ignore file: %w", err) } return nil } type VSCodeSettings map[string]interface{} func loadUserSettings(path string, fsys afero.Fs) (VSCodeSettings, error) { data, err := afero.ReadFile(fsys, path) if err != nil { return nil, errors.Errorf("failed to load settings file: %w", err) } data = jsonc.ToJSONInPlace(data) // Parse and unmarshal JSON file. var userSettings VSCodeSettings dec := json.NewDecoder(bytes.NewReader(data)) if err := dec.Decode(&userSettings); err != nil { return nil, errors.Errorf("failed to parse settings: %w", err) } return userSettings, nil } func saveUserSettings(path string, settings VSCodeSettings, fsys afero.Fs) error { // Open our jsonFile jsonFile, err := fsys.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) if err != nil { return errors.Errorf("failed to create settings file: %w", err) } defer jsonFile.Close() // Marshal JSON to file. enc := json.NewEncoder(jsonFile) enc.SetIndent("", " ") if err := enc.Encode(settings); err != nil { return errors.Errorf("failed to save settings: %w", err) } return nil } func updateJsonFile(path string, template string, fsys afero.Fs) error { userSettings, err := loadUserSettings(path, fsys) if errors.Is(err, os.ErrNotExist) || errors.Is(err, io.EOF) { return afero.WriteFile(fsys, path, []byte(template), 0644) } else if err != nil { return err } // Merge template into user settings. if err := json.Unmarshal([]byte(template), &userSettings); err != nil { return errors.Errorf("failed to copy template: %w", err) } return saveUserSettings(path, userSettings, fsys) } func writeVscodeConfig(fsys afero.Fs) error { // Create VS Code settings for Deno. if err := utils.MkdirIfNotExistFS(fsys, vscodeDir); err != nil { return err } if err := updateJsonFile(extensionsPath, vscodeExtensions, fsys); err != nil { return err } if err := updateJsonFile(settingsPath, vscodeSettings, fsys); err != nil { return err } fmt.Println("Generated VS Code settings in " + utils.Bold(settingsPath) + ". Please install the recommended extension!") return nil } func writeIntelliJConfig(fsys afero.Fs) error { if err := utils.WriteFile(denoPath, []byte(intelliJDeno), fsys); err != nil { return err } fmt.Println("Generated IntelliJ settings in " + utils.Bold(denoPath) + ". Please install the Deno plugin!") return nil }