Repository: supabase/cli
Pull request #1061 (2023-05-04 08:48:19)

fix: deno import map resolved directory paths
## What kind of change does this PR introduce?

Follow up https://github.com/supabase/cli/issues/1040

## What is the new behavior?

- serialized import map should have have lowercase fields, ie. `imports`
- unlike files, imported directories should end with `/`

unrelated
- added health checks so that `docker ps -a` is more informative

## Additional context

Tested with the following config

- `supabase/functions/import_map.json`
```json
{
  "imports": {
    "http-server": "https://deno.land/std@0.168.0/http/server.ts",
    "shared/": "../../shared/"
  }
}
```

- `supabase/functions/hello_world/index.ts`
```typescript
import { serve } from "http-server";
import { handler } from "shared/index.ts";

serve(handler, { port: 8081 });
```

- `shared/index.ts`
```typescript
export const handler = async (req) => {
  const { name } = await req.json();
  const data = {
    message: `Hello World ${name}!`,
  };

  return new Response(JSON.stringify(data), {
    headers: { "Content-Type": "application/json" },
  });
};
```

coveralls: ## Pull Request Test Coverage Report for [Build 4880833589](https://coveralls.io/builds/59609785)

* **20** of **22**   **(90.91%)**  changed or added relevant lines in **3** files are covered.
* No unchanged relevant lines lost coverage.
* Overall coverage increased (+**0.05%**) to **61.018%**

---

|  Changes Missing Coverage | Covered Lines | Changed/Added Lines | % |
| :-----|--------------|--------|---: |
| [internal/utils/deno.go](https://coveralls.io/builds/59609785/source?filename=internal%2Futils%2Fdeno.go#L304) | 6 | 8 | 75.0%
<!-- | **Total:** | **20** | **22** | **90.91%** | -->


|  Totals | [![Coverage Status](https://coveralls.io/builds/59609785/badge)](https://coveralls.io/builds/59609785) |
| :-- | --: |
| Change from base [Build 4880007128](https://coveralls.io/builds/59605767): |  0.05% |
| Covered Lines: | 4375 |
| Relevant Lines: | 7170 |

---
##### 💛  - [Coveralls](https://coveralls.io)


Patch:
diff --git a/internal/functions/serve/serve.go b/internal/functions/serve/serve.go
index 99649a15..773d5ab4 100644
--- a/internal/functions/serve/serve.go
+++ b/internal/functions/serve/serve.go
@@ -10,10 +10,12 @@ import (
 	"os"
 	"path/filepath"
 	"strings"
+	"time"
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/pkg/stdcopy"
+	"github.com/docker/go-connections/nat"
 	"github.com/joho/godotenv"
 	"github.com/spf13/afero"
 	"github.com/spf13/viper"
@@ -342,9 +344,16 @@ EOF
 	_, err = utils.DockerStart(
 		ctx,
 		container.Config{
-			Image:      utils.EdgeRuntimeImage,
-			Env:        append(env, userEnv...),
-			Entrypoint: entrypoint,
+			Image:        utils.EdgeRuntimeImage,
+			Env:          append(env, userEnv...),
+			Entrypoint:   entrypoint,
+			ExposedPorts: nat.PortSet{"8081/tcp": {}},
+			Healthcheck: &container.HealthConfig{
+				Test:     []string{"CMD", "bash", "-c", "printf \\0 > /dev/tcp/localhost/8081"},
+				Interval: 2 * time.Second,
+				Timeout:  2 * time.Second,
+				Retries:  10,
+			},
 		},
 		start.WithSyslogConfig(container.HostConfig{
 			Binds:      binds,
@@ -453,7 +462,7 @@ func bindImportMap(hostImportMapPath, dockerImportMapPath string, fsys afero.Fs)
 		if err != nil {
 			return nil, err
 		}
-		contents, err := json.Marshal(resolved)
+		contents, err := json.MarshalIndent(resolved, "", "    ")
 		if err != nil {
 			return nil, err
 		}
diff --git a/internal/start/start.go b/internal/start/start.go
index c67304ce..c78a36ac 100644
--- a/internal/start/start.go
+++ b/internal/start/start.go
@@ -348,8 +348,9 @@ EOF
 		if _, err := utils.DockerStart(
 			ctx,
 			container.Config{
-				Image: utils.GotrueImage,
-				Env:   env,
+				Image:        utils.GotrueImage,
+				Env:          env,
+				ExposedPorts: nat.PortSet{"9999/tcp": {}},
 				Healthcheck: &container.HealthConfig{
 					Test:     []string{"CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:9999/health"},
 					Interval: 2 * time.Second,
@@ -420,6 +421,7 @@ EOF
 					"/bin/sh", "-c",
 					"/app/bin/migrate && /app/bin/realtime eval 'Realtime.Release.seeds(Realtime.Repo)' && /app/bin/server",
 				},
+				ExposedPorts: nat.PortSet{"4000/tcp": {}},
 				Healthcheck: &container.HealthConfig{
 					Test:     []string{"CMD", "bash", "-c", "printf \\0 > /dev/tcp/localhost/4000"},
 					Interval: 2 * time.Second,
diff --git a/internal/utils/deno.go b/internal/utils/deno.go
index 331c6f2f..105748fe 100644
--- a/internal/utils/deno.go
+++ b/internal/utils/deno.go
@@ -216,8 +216,8 @@ func CopyDenoScripts(ctx context.Context, fsys afero.Fs) (*DenoScriptDir, error)
 }
 
 type ImportMap struct {
-	Imports map[string]string
-	Scopes  map[string]map[string]string
+	Imports map[string]string            `json:"imports"`
+	Scopes  map[string]map[string]string `json:"scopes"`
 }
 
 func NewImportMap(path string, fsys afero.Fs) (*ImportMap, error) {
@@ -300,11 +300,20 @@ func resolveHostPath(hostPath string, fsys afero.Fs) string {
 	if exists, _ := afero.Exists(fsys, rebased); !exists {
 		return hostPath
 	}
+	// Directory imports need to be suffixed with /
+	// Ref: https://deno.com/manual@v1.33.0/basics/import_maps
+	if strings.HasSuffix(hostPath, "/") {
+		rel += "/"
+	}
 	return getModulePath(rel)
 }
 
 func getModulePath(hostPath string) string {
-	return path.Join(DockerModsDir, GetPathHash(hostPath))
+	mod := path.Join(DockerModsDir, GetPathHash(hostPath))
+	if strings.HasSuffix(hostPath, "/") {
+		mod += "/"
+	}
+	return mod
 }
 
 func GetPathHash(path string) string {


Test patch:
diff --git a/internal/utils/deno_test.go b/internal/utils/deno_test.go
index 0b6708db..c6332be4 100644
--- a/internal/utils/deno_test.go
+++ b/internal/utils/deno_test.go
@@ -14,7 +14,7 @@ func TestResolveImports(t *testing.T) {
 	t.Run("resolves relative directory", func(t *testing.T) {
 		importMap := &ImportMap{
 			Imports: map[string]string{
-				"abs":     "/tmp",
+				"abs/":    "/tmp/",
 				"root":    "../../common",
 				"parent":  "../tests",
 				"child":   "child",
@@ -31,7 +31,7 @@ func TestResolveImports(t *testing.T) {
 		// Run test
 		resolved := importMap.Resolve(fsys)
 		// Check error
-		assert.Equal(t, "/home/deno/modules/e9671acd244849c57167c658fa2f969752048f7ab184a3dcf5c46cb4d56ae124", resolved.Imports["abs"])
+		assert.Equal(t, "/home/deno/modules/ac351c7174c8f47a9a9056bd96bcd71cfb980c906daee74ab9bce8308c68b811/", resolved.Imports["abs/"])
 		assert.Equal(t, "/home/deno/modules/92a5dc04bd6f9fb8f29f8066fed8a5c1e81bc59ad48a11283b63736867e4f2a8", resolved.Imports["root"])
 		assert.Equal(t, "/home/deno/modules/faaed96206118cf98625ea8065b6b3864f8cf9484814c423b58ebaa9b2d1e47b", resolved.Imports["parent"])
 		assert.Equal(t, "child", resolved.Imports["child"])