Skip to content

Commit b1b3d1f

Browse files
authored
fix: resolve legacy import map (#3395)
1 parent 6916a8e commit b1b3d1f

File tree

5 files changed

+95
-12
lines changed

5 files changed

+95
-12
lines changed

pkg/function/deploy.go

Lines changed: 58 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,9 @@ func writeForm(form *multipart.Writer, meta api.FunctionDeployMetadata, fsys fs.
163163
if err := importMap.Parse(data); err != nil {
164164
return err
165165
}
166+
if err := importMap.Resolve(imPath, fsys); err != nil {
167+
return err
168+
}
166169
// TODO: replace with addFile once edge runtime supports jsonc
167170
fmt.Fprintf(os.Stderr, "Uploading asset (%s): %s\n", *meta.Name, imPath)
168171
f, err := form.CreateFormFile("file", imPath)
@@ -201,6 +204,41 @@ func (m *ImportMap) Parse(data []byte) error {
201204
return nil
202205
}
203206

207+
func (m *ImportMap) Resolve(imPath string, fsys fs.FS) error {
208+
// Resolve all paths relative to current file
209+
for k, v := range m.Imports {
210+
m.Imports[k] = resolveHostPath(imPath, v, fsys)
211+
}
212+
for module, mapping := range m.Scopes {
213+
for k, v := range mapping {
214+
m.Scopes[module][k] = resolveHostPath(imPath, v, fsys)
215+
}
216+
}
217+
return nil
218+
}
219+
220+
func resolveHostPath(jsonPath, hostPath string, fsys fs.FS) string {
221+
// Leave absolute paths unchanged
222+
if path.IsAbs(hostPath) {
223+
return hostPath
224+
}
225+
resolved := path.Join(path.Dir(jsonPath), hostPath)
226+
if _, err := fs.Stat(fsys, filepath.FromSlash(resolved)); err != nil {
227+
// Leave URLs unchanged
228+
return hostPath
229+
}
230+
// Directory imports need to be suffixed with /
231+
// Ref: https://deno.com/[email protected]/basics/import_maps
232+
if strings.HasSuffix(hostPath, "/") {
233+
resolved += "/"
234+
}
235+
// Relative imports must be prefixed with ./ or ../
236+
if !path.IsAbs(resolved) {
237+
resolved = "./" + resolved
238+
}
239+
return resolved
240+
}
241+
204242
// Ref: https://regex101.com/r/DfBdJA/1
205243
var importPathPattern = regexp.MustCompile(`(?i)(?:import|export)\s+(?:{[^{}]+}|.*?)\s*(?:from)?\s*['"](.*?)['"]|import\(\s*['"](.*?)['"]\)`)
206244

@@ -237,22 +275,35 @@ func walkImportPaths(srcPath string, importMap ImportMap, readFile func(curr str
237275
}
238276
mod = strings.TrimSpace(mod)
239277
// Substitute kv from import map
278+
substituted := false
240279
for k, v := range importMap.Imports {
241280
if strings.HasPrefix(mod, k) {
242281
mod = v + mod[len(k):]
282+
substituted = true
243283
}
244284
}
245-
// Deno import path must begin with these prefixes
246-
if strings.HasPrefix(mod, "./") || strings.HasPrefix(mod, "../") {
247-
mod = path.Join(path.Dir(curr), mod)
248-
} else if !strings.HasPrefix(mod, "/") {
285+
// Ignore URLs and directories
286+
if len(path.Ext(mod)) == 0 {
249287
continue
250288
}
251-
if len(path.Ext(mod)) > 0 {
252-
// Cleans import path to help detect duplicates
253-
q = append(q, path.Clean(mod))
289+
// Deno import path must begin with one of these prefixes
290+
if !isRelPath(mod) && !isAbsPath(mod) {
291+
continue
254292
}
293+
if isRelPath(mod) && !substituted {
294+
mod = path.Join(path.Dir(curr), mod)
295+
}
296+
// Cleans import path to help detect duplicates
297+
q = append(q, path.Clean(mod))
255298
}
256299
}
257300
return nil
258301
}
302+
303+
func isRelPath(mod string) bool {
304+
return strings.HasPrefix(mod, "./") || strings.HasPrefix(mod, "../")
305+
}
306+
307+
func isAbsPath(mod string) bool {
308+
return strings.HasPrefix(mod, "/")
309+
}

pkg/function/deploy_test.go

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,7 @@ func TestImportPaths(t *testing.T) {
4949
fsys.On("ReadFile", "testdata/modules/imports.ts").Once()
5050
fsys.On("ReadFile", "testdata/geometries/Geometries.js").Once()
5151
// Run test
52-
im := ImportMap{}
53-
err := walkImportPaths("testdata/modules/imports.ts", im, fsys.ReadFile)
52+
err := walkImportPaths("testdata/modules/imports.ts", ImportMap{}, fsys.ReadFile)
5453
// Check error
5554
assert.NoError(t, err)
5655
fsys.AssertExpectations(t)
@@ -65,10 +64,33 @@ func TestImportPaths(t *testing.T) {
6564
fsys.On("ReadFile", "testdata/shared/whatever.ts").Once()
6665
fsys.On("ReadFile", "testdata/shared/mod.ts").Once()
6766
fsys.On("ReadFile", "testdata/nested/index.ts").Once()
68-
// Run test
67+
// Setup deno.json
6968
im := ImportMap{Imports: map[string]string{
7069
"module-name/": "../shared/",
7170
}}
71+
assert.NoError(t, im.Resolve("testdata/modules/deno.json", testImports))
72+
// Run test
73+
err := walkImportPaths("testdata/modules/imports.ts", im, fsys.ReadFile)
74+
// Check error
75+
assert.NoError(t, err)
76+
fsys.AssertExpectations(t)
77+
})
78+
79+
t.Run("resolves legacy import map", func(t *testing.T) {
80+
// Setup in-memory fs
81+
fsys := MockFS{}
82+
fsys.On("ReadFile", "/modules/my-module.ts").Once()
83+
fsys.On("ReadFile", "testdata/modules/imports.ts").Once()
84+
fsys.On("ReadFile", "testdata/geometries/Geometries.js").Once()
85+
fsys.On("ReadFile", "testdata/shared/whatever.ts").Once()
86+
fsys.On("ReadFile", "testdata/shared/mod.ts").Once()
87+
fsys.On("ReadFile", "testdata/nested/index.ts").Once()
88+
// Setup legacy import map
89+
im := ImportMap{Imports: map[string]string{
90+
"module-name/": "./shared/",
91+
}}
92+
assert.NoError(t, im.Resolve("testdata/import_map.json", testImports))
93+
// Run test
7294
err := walkImportPaths("testdata/modules/imports.ts", im, fsys.ReadFile)
7395
// Check error
7496
assert.NoError(t, err)
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,5 @@
1-
{}
1+
{
2+
"imports": {
3+
"module": "jsr:@supabase/functions-js/edge-runtime.d.ts"
4+
}
5+
}

pkg/function/testdata/nested/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import "module";

pkg/function/testdata/writes_import_map.form

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@ Content-Disposition: form-data; name="metadata"
77
Content-Disposition: form-data; name="file"; filename="testdata/nested/deno.json"
88
Content-Type: application/octet-stream
99

10-
{}
10+
{
11+
"imports": {
12+
"module": "jsr:@supabase/functions-js/edge-runtime.d.ts"
13+
}
14+
}
1115

1216
--test
1317
Content-Disposition: form-data; name="file"; filename="testdata/geometries/Geometries.js"
@@ -18,3 +22,4 @@ Content-Type: application/octet-stream
1822
Content-Disposition: form-data; name="file"; filename="testdata/nested/index.ts"
1923
Content-Type: application/octet-stream
2024

25+
import "module";

0 commit comments

Comments
 (0)