• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

supabase / cli / 14432158384

13 Apr 2025 06:26PM UTC coverage: 51.023% (+0.07%) from 50.957%
14432158384

Pull #3430

github

web-flow
Merge cae5d76b5 into 2afb164a9
Pull Request #3430: feat: load import map reference in deno config

118 of 141 new or added lines in 3 files covered. (83.69%)

2 existing lines in 1 file now uncovered.

6985 of 13690 relevant lines covered (51.02%)

184.46 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.68
/pkg/function/deno.go
1
package function
2

3
import (
4
        "bytes"
5
        "encoding/json"
6
        "fmt"
7
        "io"
8
        "io/fs"
9
        "os"
10
        "path"
11
        "path/filepath"
12
        "regexp"
13
        "strings"
14

15
        "github.com/go-errors/errors"
16
        "github.com/tidwall/jsonc"
17
)
18

19
type ImportMap struct {
20
        Imports map[string]string            `json:"imports"`
21
        Scopes  map[string]map[string]string `json:"scopes"`
22
        // Fallback reference for deno.json
23
        ImportMap string `json:"importMap"`
24
}
25

26
func (m *ImportMap) LoadAsDeno(imPath string, fsys fs.FS, opts ...func(string, io.Reader) error) error {
5✔
27
        if err := m.Load(imPath, fsys, opts...); err != nil {
6✔
28
                return err
1✔
29
        }
1✔
30
        if name := path.Base(imPath); isDeno(name) && m.IsReference() {
6✔
31
                imPath = path.Join(path.Dir(imPath), m.ImportMap)
2✔
32
                if err := m.Load(imPath, fsys, opts...); err != nil {
3✔
33
                        return err
1✔
34
                }
1✔
35
        }
36
        return nil
3✔
37
}
38

39
func isDeno(name string) bool {
4✔
40
        return strings.EqualFold(name, "deno.json") ||
4✔
41
                strings.EqualFold(name, "deno.jsonc")
4✔
42
}
4✔
43

44
func (m *ImportMap) IsReference() bool {
4✔
45
        // Ref: https://github.com/denoland/deno/blob/main/cli/schemas/config-file.v1.json#L273
4✔
46
        return len(m.Imports) == 0 && len(m.Scopes) == 0 && len(m.ImportMap) > 0
4✔
47
}
4✔
48

49
func (m *ImportMap) Load(imPath string, fsys fs.FS, opts ...func(string, io.Reader) error) error {
9✔
50
        data, err := fs.ReadFile(fsys, filepath.FromSlash(imPath))
9✔
51
        if err != nil {
11✔
52
                return errors.Errorf("failed to load import map: %w", err)
2✔
53
        }
2✔
54
        if err := m.Parse(data); err != nil {
7✔
NEW
55
                return err
×
NEW
56
        }
×
57
        if err := m.Resolve(imPath, fsys); err != nil {
7✔
NEW
58
                return err
×
NEW
59
        }
×
60
        for _, apply := range opts {
8✔
61
                if err := apply(imPath, bytes.NewReader(data)); err != nil {
1✔
NEW
62
                        return err
×
NEW
63
                }
×
64
        }
65
        return nil
7✔
66
}
67

68
func (m *ImportMap) Parse(data []byte) error {
7✔
69
        data = jsonc.ToJSONInPlace(data)
7✔
70
        decoder := json.NewDecoder(bytes.NewReader(data))
7✔
71
        if err := decoder.Decode(&m); err != nil {
7✔
NEW
72
                return errors.Errorf("failed to parse import map: %w", err)
×
NEW
73
        }
×
74
        return nil
7✔
75
}
76

77
func (m *ImportMap) Resolve(imPath string, fsys fs.FS) error {
9✔
78
        // Resolve all paths relative to current file
9✔
79
        for k, v := range m.Imports {
19✔
80
                m.Imports[k] = resolveHostPath(imPath, v, fsys)
10✔
81
        }
10✔
82
        for module, mapping := range m.Scopes {
10✔
83
                for k, v := range mapping {
2✔
84
                        m.Scopes[module][k] = resolveHostPath(imPath, v, fsys)
1✔
85
                }
1✔
86
        }
87
        return nil
9✔
88
}
89

90
func resolveHostPath(jsonPath, hostPath string, fsys fs.FS) string {
11✔
91
        // Leave absolute paths unchanged
11✔
92
        if path.IsAbs(hostPath) {
12✔
93
                return hostPath
1✔
94
        }
1✔
95
        resolved := path.Join(path.Dir(jsonPath), hostPath)
10✔
96
        if _, err := fs.Stat(fsys, filepath.FromSlash(resolved)); err != nil {
14✔
97
                // Leave URLs unchanged
4✔
98
                return hostPath
4✔
99
        }
4✔
100
        // Directory imports need to be suffixed with /
101
        // Ref: https://deno.com/manual@v1.33.0/basics/import_maps
102
        if strings.HasSuffix(hostPath, "/") {
9✔
103
                resolved += "/"
3✔
104
        }
3✔
105
        // Relative imports must be prefixed with ./ or ../
106
        if !path.IsAbs(resolved) {
12✔
107
                resolved = "./" + resolved
6✔
108
        }
6✔
109
        return resolved
6✔
110
}
111

112
// Ref: https://regex101.com/r/DfBdJA/1
113
var importPathPattern = regexp.MustCompile(`(?i)(?:import|export)\s+(?:{[^{}]+}|.*?)\s*(?:from)?\s*['"](.*?)['"]|import\(\s*['"](.*?)['"]\)`)
114

115
func (importMap *ImportMap) WalkImportPaths(srcPath string, readFile func(curr string, w io.Writer) error) error {
4✔
116
        seen := map[string]struct{}{}
4✔
117
        // DFS because it's more efficient to pop from end of array
4✔
118
        q := make([]string, 1)
4✔
119
        q[0] = srcPath
4✔
120
        for len(q) > 0 {
31✔
121
                curr := q[len(q)-1]
27✔
122
                q = q[:len(q)-1]
27✔
123
                // Assume no file is symlinked
27✔
124
                if _, ok := seen[curr]; ok {
38✔
125
                        continue
11✔
126
                }
127
                seen[curr] = struct{}{}
16✔
128
                // Read into memory for regex match later
16✔
129
                var buf bytes.Buffer
16✔
130
                if err := readFile(curr, &buf); errors.Is(err, os.ErrNotExist) {
21✔
131
                        fmt.Fprintln(os.Stderr, "WARN:", err)
5✔
132
                        continue
5✔
133
                } else if err != nil {
11✔
NEW
134
                        return err
×
NEW
135
                }
×
136
                // Traverse all modules imported by the current source file
137
                for _, matches := range importPathPattern.FindAllStringSubmatch(buf.String(), -1) {
168✔
138
                        if len(matches) < 3 {
157✔
NEW
139
                                continue
×
140
                        }
141
                        // Matches 'from' clause if present, else fallback to 'import'
142
                        mod := matches[1]
157✔
143
                        if len(mod) == 0 {
163✔
144
                                mod = matches[2]
6✔
145
                        }
6✔
146
                        mod = strings.TrimSpace(mod)
157✔
147
                        // Substitute kv from import map
157✔
148
                        substituted := false
157✔
149
                        for k, v := range importMap.Imports {
264✔
150
                                if strings.HasPrefix(mod, k) {
112✔
151
                                        mod = v + mod[len(k):]
5✔
152
                                        substituted = true
5✔
153
                                }
5✔
154
                        }
155
                        // Ignore URLs and directories, assuming no sloppy imports
156
                        // https://github.com/denoland/deno/issues/2506#issuecomment-2727635545
157
                        if len(path.Ext(mod)) == 0 {
288✔
158
                                continue
131✔
159
                        }
160
                        // Deno import path must begin with one of these prefixes
161
                        if !isRelPath(mod) && !isAbsPath(mod) {
29✔
162
                                continue
3✔
163
                        }
164
                        if isRelPath(mod) && !substituted {
33✔
165
                                mod = path.Join(path.Dir(curr), mod)
10✔
166
                        }
10✔
167
                        // Cleans import path to help detect duplicates
168
                        q = append(q, path.Clean(mod))
23✔
169
                }
170
        }
171
        return nil
4✔
172
}
173

174
func isRelPath(mod string) bool {
49✔
175
        return strings.HasPrefix(mod, "./") || strings.HasPrefix(mod, "../")
49✔
176
}
49✔
177

178
func isAbsPath(mod string) bool {
12✔
179
        return strings.HasPrefix(mod, "/")
12✔
180
}
12✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc