Skip to content

Commit f1c3b1c

Browse files
authored
refactor(cli): move Deno/IntelliJ prompts from init to functions new (#4598)
* refactor(cli): move Deno/IntelliJ prompts from init to functions new Move IDE configuration prompts from `supabase init` to `supabase functions new` when creating the first function, making the init command simpler and more focused. This addresses user feedback that Deno/IntelliJ prompts were irrelevant during project initialization. Changes: - Remove interactive Deno/IntelliJ prompts from `supabase init` - Add prompts to `supabase functions new` when creating the first function - Export WriteVscodeConfig and WriteIntelliJConfig for reuse - Improve extension installation messages with explicit marketplace links - Update tests to reflect the new behavior The init command now only handles: - Creating config.toml - Updating .gitignore IDE settings are now prompted contextually when users first create a function, with VS Code as the default option. Fixes DEVWF-30, DEVWF-108, DEVWF-869 * chore: remove unecessary comments
1 parent 34b8667 commit f1c3b1c

File tree

6 files changed

+116
-44
lines changed

6 files changed

+116
-44
lines changed

cmd/init.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ package cmd
22

33
import (
44
"fmt"
5-
"os"
6-
"os/signal"
75

86
"github.com/spf13/afero"
97
"github.com/spf13/cobra"
@@ -42,8 +40,7 @@ var (
4240
if !cmd.Flags().Changed("with-intellij-settings") {
4341
createIntellijSettings = nil
4442
}
45-
ctx, _ := signal.NotifyContext(cmd.Context(), os.Interrupt)
46-
return _init.Run(ctx, fsys, createVscodeSettings, createIntellijSettings, initParams)
43+
return _init.Run(fsys, createVscodeSettings, createIntellijSettings, initParams)
4744
},
4845
PostRun: func(cmd *cobra.Command, args []string) {
4946
fmt.Println("Finished " + utils.Aqua("supabase init") + ".")

internal/bootstrap/bootstrap.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ func Run(ctx context.Context, starter StarterTemplate, fsys afero.Fs, options ..
6060
if err := downloadSample(ctx, client, starter.Url, fsys); err != nil {
6161
return err
6262
}
63-
} else if err := initBlank.Run(ctx, fsys, nil, nil, utils.InitParams{Overwrite: true}); err != nil {
63+
} else if err := initBlank.Run(fsys, nil, nil, utils.InitParams{Overwrite: true}); err != nil {
6464
return err
6565
}
6666
// 1. Login

internal/functions/new/new.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010

1111
"github.com/go-errors/errors"
1212
"github.com/spf13/afero"
13+
_init "github.com/supabase/cli/internal/init"
1314
"github.com/supabase/cli/internal/utils"
1415
"github.com/supabase/cli/internal/utils/flags"
1516
)
@@ -38,6 +39,8 @@ func Run(ctx context.Context, slug string, fsys afero.Fs) error {
3839
if err := utils.ValidateFunctionSlug(slug); err != nil {
3940
return err
4041
}
42+
isFirstFunction := isFirstFunctionCreation(fsys)
43+
4144
// 2. Create new function.
4245
funcDir := filepath.Join(utils.FunctionsDir, slug)
4346
if err := utils.MkdirIfNotExistFS(fsys, funcDir); err != nil {
@@ -61,6 +64,44 @@ func Run(ctx context.Context, slug string, fsys afero.Fs) error {
6164
return errors.Errorf("failed to create .npmrc config: %w", err)
6265
}
6366
fmt.Println("Created new Function at " + utils.Bold(funcDir))
67+
68+
if isFirstFunction {
69+
if err := promptForIDESettings(ctx, fsys); err != nil {
70+
return err
71+
}
72+
}
73+
return nil
74+
}
75+
76+
// Checks if this is the first function being created.
77+
// Returns true if the functions directory doesn't exist or is empty.
78+
func isFirstFunctionCreation(fsys afero.Fs) bool {
79+
entries, err := afero.ReadDir(fsys, utils.FunctionsDir)
80+
if err != nil {
81+
// Directory doesn't exist, this is the first function
82+
return true
83+
}
84+
// Check if there are any subdirectories (existing functions)
85+
for _, entry := range entries {
86+
if entry.IsDir() {
87+
return false
88+
}
89+
}
90+
return true
91+
}
92+
93+
func promptForIDESettings(ctx context.Context, fsys afero.Fs) error {
94+
console := utils.NewConsole()
95+
if isVscode, err := console.PromptYesNo(ctx, "Generate VS Code settings for Deno?", true); err != nil {
96+
return err
97+
} else if isVscode {
98+
return _init.WriteVscodeConfig(fsys)
99+
}
100+
if isIntelliJ, err := console.PromptYesNo(ctx, "Generate IntelliJ IDEA settings for Deno?", false); err != nil {
101+
return err
102+
} else if isIntelliJ {
103+
return _init.WriteIntelliJConfig(fsys)
104+
}
64105
return nil
65106
}
66107

internal/functions/new/new_test.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,52 @@ func TestNewCommand(t *testing.T) {
6060
assert.Error(t, Run(context.Background(), "test-func", fsys))
6161
})
6262
}
63+
64+
func TestIsFirstFunctionCreation(t *testing.T) {
65+
t.Run("returns true when functions directory does not exist", func(t *testing.T) {
66+
// Setup in-memory fs without functions directory
67+
fsys := afero.NewMemMapFs()
68+
// Run test
69+
assert.True(t, isFirstFunctionCreation(fsys))
70+
})
71+
72+
t.Run("returns true when functions directory is empty", func(t *testing.T) {
73+
// Setup in-memory fs with empty functions directory
74+
fsys := afero.NewMemMapFs()
75+
require.NoError(t, fsys.MkdirAll(utils.FunctionsDir, 0755))
76+
// Run test
77+
assert.True(t, isFirstFunctionCreation(fsys))
78+
})
79+
80+
t.Run("returns true when functions directory has only files", func(t *testing.T) {
81+
// Setup in-memory fs with functions directory containing only files
82+
fsys := afero.NewMemMapFs()
83+
require.NoError(t, fsys.MkdirAll(utils.FunctionsDir, 0755))
84+
// Create files (not directories) in functions directory
85+
require.NoError(t, afero.WriteFile(fsys, filepath.Join(utils.FunctionsDir, "import_map.json"), []byte("{}"), 0644))
86+
require.NoError(t, afero.WriteFile(fsys, filepath.Join(utils.FunctionsDir, ".env"), []byte(""), 0644))
87+
// Run test
88+
assert.True(t, isFirstFunctionCreation(fsys))
89+
})
90+
91+
t.Run("returns false when functions directory has subdirectories", func(t *testing.T) {
92+
// Setup in-memory fs with existing function
93+
fsys := afero.NewMemMapFs()
94+
existingFuncDir := filepath.Join(utils.FunctionsDir, "existing-func")
95+
require.NoError(t, fsys.MkdirAll(existingFuncDir, 0755))
96+
require.NoError(t, afero.WriteFile(fsys, filepath.Join(existingFuncDir, "index.ts"), []byte(""), 0644))
97+
// Run test
98+
assert.False(t, isFirstFunctionCreation(fsys))
99+
})
100+
101+
t.Run("returns false when multiple functions exist", func(t *testing.T) {
102+
// Setup in-memory fs with multiple existing functions
103+
fsys := afero.NewMemMapFs()
104+
func1Dir := filepath.Join(utils.FunctionsDir, "func1")
105+
func2Dir := filepath.Join(utils.FunctionsDir, "func2")
106+
require.NoError(t, fsys.MkdirAll(func1Dir, 0755))
107+
require.NoError(t, fsys.MkdirAll(func2Dir, 0755))
108+
// Run test
109+
assert.False(t, isFirstFunctionCreation(fsys))
110+
})
111+
}

internal/init/init.go

Lines changed: 13 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package init
22

33
import (
44
"bytes"
5-
"context"
65
_ "embed"
76
"encoding/json"
87
"fmt"
@@ -33,7 +32,7 @@ var (
3332
intelliJDeno string
3433
)
3534

36-
func Run(ctx context.Context, fsys afero.Fs, createVscodeSettings, createIntellijSettings *bool, params utils.InitParams) error {
35+
func Run(fsys afero.Fs, createVscodeSettings, createIntellijSettings *bool, params utils.InitParams) error {
3736
// 1. Write `config.toml`.
3837
if err := utils.InitConfig(params, fsys); err != nil {
3938
if errors.Is(err, os.ErrExist) {
@@ -49,27 +48,12 @@ func Run(ctx context.Context, fsys afero.Fs, createVscodeSettings, createIntelli
4948
}
5049
}
5150

52-
// 3. Generate VS Code settings.
53-
if createVscodeSettings != nil {
54-
if *createVscodeSettings {
55-
return writeVscodeConfig(fsys)
56-
}
57-
} else if createIntellijSettings != nil {
58-
if *createIntellijSettings {
59-
return writeIntelliJConfig(fsys)
60-
}
61-
} else {
62-
console := utils.NewConsole()
63-
if isVscode, err := console.PromptYesNo(ctx, "Generate VS Code settings for Deno?", false); err != nil {
64-
return err
65-
} else if isVscode {
66-
return writeVscodeConfig(fsys)
67-
}
68-
if isIntelliJ, err := console.PromptYesNo(ctx, "Generate IntelliJ Settings for Deno?", false); err != nil {
69-
return err
70-
} else if isIntelliJ {
71-
return writeIntelliJConfig(fsys)
72-
}
51+
// 3. Generate VS Code or IntelliJ settings if explicitly requested via flags.
52+
if createVscodeSettings != nil && *createVscodeSettings {
53+
return WriteVscodeConfig(fsys)
54+
}
55+
if createIntellijSettings != nil && *createIntellijSettings {
56+
return WriteIntelliJConfig(fsys)
7357
}
7458
return nil
7559
}
@@ -146,7 +130,7 @@ func updateJsonFile(path string, template string, fsys afero.Fs) error {
146130
return saveUserSettings(path, userSettings, fsys)
147131
}
148132

149-
func writeVscodeConfig(fsys afero.Fs) error {
133+
func WriteVscodeConfig(fsys afero.Fs) error {
150134
// Create VS Code settings for Deno.
151135
if err := utils.MkdirIfNotExistFS(fsys, vscodeDir); err != nil {
152136
return err
@@ -157,14 +141,16 @@ func writeVscodeConfig(fsys afero.Fs) error {
157141
if err := updateJsonFile(settingsPath, vscodeSettings, fsys); err != nil {
158142
return err
159143
}
160-
fmt.Println("Generated VS Code settings in " + utils.Bold(settingsPath) + ". Please install the recommended extension!")
144+
fmt.Println("Generated VS Code settings in " + utils.Bold(settingsPath) + ".")
145+
fmt.Println("Please install the Deno extension for VS Code: " + utils.Aqua("https://marketplace.visualstudio.com/items?itemName=denoland.vscode-deno"))
161146
return nil
162147
}
163148

164-
func writeIntelliJConfig(fsys afero.Fs) error {
149+
func WriteIntelliJConfig(fsys afero.Fs) error {
165150
if err := utils.WriteFile(denoPath, []byte(intelliJDeno), fsys); err != nil {
166151
return err
167152
}
168-
fmt.Println("Generated IntelliJ settings in " + utils.Bold(denoPath) + ". Please install the Deno plugin!")
153+
fmt.Println("Generated IntelliJ settings in " + utils.Bold(denoPath) + ".")
154+
fmt.Println("Please install the Deno plugin for IntelliJ: " + utils.Aqua("https://plugins.jetbrains.com/plugin/14382-deno"))
169155
return nil
170156
}

internal/init/init_test.go

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package init
22

33
import (
4-
"context"
54
"encoding/json"
65
"os"
76
"testing"
@@ -20,7 +19,7 @@ func TestInitCommand(t *testing.T) {
2019
fsys := &afero.MemMapFs{}
2120
require.NoError(t, fsys.Mkdir(".git", 0755))
2221
// Run test
23-
assert.NoError(t, Run(context.Background(), fsys, nil, nil, utils.InitParams{}))
22+
assert.NoError(t, Run(fsys, nil, nil, utils.InitParams{}))
2423
// Validate generated config.toml
2524
exists, err := afero.Exists(fsys, utils.ConfigPath)
2625
assert.NoError(t, err)
@@ -48,14 +47,14 @@ func TestInitCommand(t *testing.T) {
4847
_, err := fsys.Create(utils.ConfigPath)
4948
require.NoError(t, err)
5049
// Run test
51-
assert.Error(t, Run(context.Background(), fsys, nil, nil, utils.InitParams{}))
50+
assert.Error(t, Run(fsys, nil, nil, utils.InitParams{}))
5251
})
5352

5453
t.Run("throws error on permission denied", func(t *testing.T) {
5554
// Setup in-memory fs
5655
fsys := &fstest.OpenErrorFs{DenyPath: utils.ConfigPath}
5756
// Run test
58-
err := Run(context.Background(), fsys, nil, nil, utils.InitParams{})
57+
err := Run(fsys, nil, nil, utils.InitParams{})
5958
// Check error
6059
assert.ErrorIs(t, err, os.ErrPermission)
6160
})
@@ -64,14 +63,14 @@ func TestInitCommand(t *testing.T) {
6463
// Setup read-only fs
6564
fsys := afero.NewReadOnlyFs(afero.NewMemMapFs())
6665
// Run test
67-
assert.Error(t, Run(context.Background(), fsys, nil, nil, utils.InitParams{}))
66+
assert.Error(t, Run(fsys, nil, nil, utils.InitParams{}))
6867
})
6968

7069
t.Run("creates vscode settings file", func(t *testing.T) {
7170
// Setup in-memory fs
7271
fsys := &afero.MemMapFs{}
7372
// Run test
74-
assert.NoError(t, Run(context.Background(), fsys, cast.Ptr(true), nil, utils.InitParams{}))
73+
assert.NoError(t, Run(fsys, cast.Ptr(true), nil, utils.InitParams{}))
7574
// Validate generated vscode settings
7675
exists, err := afero.Exists(fsys, settingsPath)
7776
assert.NoError(t, err)
@@ -85,7 +84,7 @@ func TestInitCommand(t *testing.T) {
8584
// Setup in-memory fs
8685
fsys := &afero.MemMapFs{}
8786
// Run test
88-
assert.NoError(t, Run(context.Background(), fsys, cast.Ptr(false), nil, utils.InitParams{}))
87+
assert.NoError(t, Run(fsys, cast.Ptr(false), nil, utils.InitParams{}))
8988
// Validate vscode settings file isn't generated
9089
exists, err := afero.Exists(fsys, settingsPath)
9190
assert.NoError(t, err)
@@ -99,7 +98,7 @@ func TestInitCommand(t *testing.T) {
9998
// Setup in-memory fs
10099
fsys := &afero.MemMapFs{}
101100
// Run test
102-
assert.NoError(t, Run(context.Background(), fsys, nil, cast.Ptr(true), utils.InitParams{}))
101+
assert.NoError(t, Run(fsys, nil, cast.Ptr(true), utils.InitParams{}))
103102
// Validate generated intellij deno config
104103
exists, err := afero.Exists(fsys, denoPath)
105104
assert.NoError(t, err)
@@ -110,7 +109,7 @@ func TestInitCommand(t *testing.T) {
110109
// Setup in-memory fs
111110
fsys := &afero.MemMapFs{}
112111
// Run test
113-
assert.NoError(t, Run(context.Background(), fsys, nil, cast.Ptr(false), utils.InitParams{}))
112+
assert.NoError(t, Run(fsys, nil, cast.Ptr(false), utils.InitParams{}))
114113
// Validate intellij deno config file isn't generated
115114
exists, err := afero.Exists(fsys, denoPath)
116115
assert.NoError(t, err)
@@ -170,7 +169,7 @@ func TestWriteVSCodeConfig(t *testing.T) {
170169
// Setup in-memory fs
171170
fsys := afero.NewMemMapFs()
172171
// Run test
173-
err := writeVscodeConfig(afero.NewReadOnlyFs(fsys))
172+
err := WriteVscodeConfig(afero.NewReadOnlyFs(fsys))
174173
// Check error
175174
assert.ErrorIs(t, err, os.ErrPermission)
176175
})
@@ -179,7 +178,7 @@ func TestWriteVSCodeConfig(t *testing.T) {
179178
// Setup in-memory fs
180179
fsys := &fstest.OpenErrorFs{DenyPath: extensionsPath}
181180
// Run test
182-
err := writeVscodeConfig(fsys)
181+
err := WriteVscodeConfig(fsys)
183182
// Check error
184183
assert.ErrorIs(t, err, os.ErrPermission)
185184
})
@@ -188,7 +187,7 @@ func TestWriteVSCodeConfig(t *testing.T) {
188187
// Setup in-memory fs
189188
fsys := &fstest.OpenErrorFs{DenyPath: settingsPath}
190189
// Run test
191-
err := writeVscodeConfig(fsys)
190+
err := WriteVscodeConfig(fsys)
192191
// Check error
193192
assert.ErrorIs(t, err, os.ErrPermission)
194193
})

0 commit comments

Comments
 (0)