Skip to content

Commit 72a6b59

Browse files
authored
Use deno.json(c) on /gh/* pattern (#1085)
1 parent b2b237a commit 72a6b59

File tree

5 files changed

+177
-17
lines changed

5 files changed

+177
-17
lines changed

server/build.go

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -460,9 +460,22 @@ func (ctx *BuildContext) buildModule(analyzeMode bool) (meta *BuildMeta, include
460460
}
461461
}
462462

463-
// resolve specifier using the `imports` field of package.json
463+
// resolve specifier using the `imports` field of package.json/deno.json
464464
if len(pkgJson.Imports) > 0 {
465-
if v, ok := pkgJson.Imports[specifier]; ok {
465+
var v any
466+
var ok bool
467+
v, ok = pkgJson.Imports[specifier]
468+
if !ok {
469+
// check tailing slash
470+
pkgName, _, subPath, _ := splitEsmPath(specifier)
471+
v, ok = pkgJson.Imports[pkgName]
472+
if ok && subPath != "" {
473+
if s, ok := v.(string); ok {
474+
v = s + "/" + subPath
475+
}
476+
}
477+
}
478+
if ok {
466479
if s, ok := v.(string); ok {
467480
specifier = s
468481
} else if m, ok := v.(map[string]interface{}); ok {

server/build_resolver.go

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package server
33
import (
44
"crypto/sha1"
55
"encoding/hex"
6+
"errors"
67
"fmt"
78
"path"
89
"sort"
@@ -715,25 +716,37 @@ func (ctx *BuildContext) resolveExternalModule(specifier string, kind api.Resolv
715716
return
716717
}
717718

718-
// common npm dependency
719-
pkgName, version, subPath, _ := splitEsmPath(specifier)
720-
if version == "" {
719+
var pkgName, pkgVersion, subPath string
720+
721+
// jsr dependency
722+
if strings.HasPrefix(specifier, "jsr:") {
723+
pkgName, pkgVersion, subPath, _ = splitEsmPath(specifier[4:])
724+
if !strings.HasPrefix(pkgName, "@") || !strings.ContainsRune(pkgName, '/') {
725+
return specifier, errors.New("invalid `jsr:` dependency:" + specifier)
726+
}
727+
scope, name := utils.SplitByFirstByte(pkgName, '/')
728+
pkgName = "@jsr/" + scope[1:] + "__" + name
729+
} else {
730+
pkgName, pkgVersion, subPath, _ = splitEsmPath(specifier)
731+
}
732+
733+
if pkgVersion == "" {
721734
if pkgName == ctx.esm.PkgName {
722-
version = ctx.esm.PkgVersion
735+
pkgVersion = ctx.esm.PkgVersion
723736
} else if pkgVerson, ok := ctx.args.deps[pkgName]; ok {
724-
version = pkgVerson
737+
pkgVersion = pkgVerson
725738
} else if v, ok := ctx.pkgJson.Dependencies[pkgName]; ok {
726-
version = strings.TrimSpace(v)
739+
pkgVersion = strings.TrimSpace(v)
727740
} else if v, ok := ctx.pkgJson.PeerDependencies[pkgName]; ok {
728-
version = strings.TrimSpace(v)
741+
pkgVersion = strings.TrimSpace(v)
729742
} else {
730-
version = "latest"
743+
pkgVersion = "latest"
731744
}
732745
}
733746

734747
dep := EsmPath{
735748
PkgName: pkgName,
736-
PkgVersion: version,
749+
PkgVersion: pkgVersion,
737750
SubPath: subPath,
738751
SubModuleName: stripEntryModuleExt(subPath),
739752
}
@@ -742,7 +755,7 @@ func (ctx *BuildContext) resolveExternalModule(specifier string, kind api.Resolv
742755
// e.g. "@mark/html": "npm:@jsr/mark__html@^1.0.0"
743756
// e.g. "tslib": "git+https://github.com/microsoft/tslib.git#v2.3.0"
744757
// e.g. "react": "github:facebook/react#v18.2.0"
745-
p, err := resolveDependencyVersion(version)
758+
p, err := resolveDependencyVersion(pkgVersion)
746759
if err != nil {
747760
resolvedPath = fmt.Sprintf("/error.js?type=%s&name=%s&importer=%s", strings.ReplaceAll(err.Error(), " ", "-"), pkgName, ctx.esm.Specifier())
748761
return
@@ -825,7 +838,7 @@ func (ctx *BuildContext) resolveExternalModule(specifier string, kind api.Resolv
825838
if strings.ContainsRune(dep.PkgVersion, '|') || strings.ContainsRune(dep.PkgVersion, ' ') {
826839
// fetch the latest version of the package based on the semver range
827840
var p *PackageJSON
828-
_, p, err = ctx.lookupDep(pkgName+"@"+version, false)
841+
_, p, err = ctx.lookupDep(pkgName+"@"+pkgVersion, false)
829842
if err != nil {
830843
return
831844
}

server/common/jsonc.go

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package common
2+
3+
// StripJSONC strips out comments and trailing commas and convert the input to a
4+
// valid JSON per the official spec: https://tools.ietf.org/html/rfc8259
5+
//
6+
// The resulting JSON will always be the same length as the input and it will
7+
// include all of the same line breaks at matching offsets. This is to ensure
8+
// the result can be later processed by a external parser and that that
9+
// parser will report messages or errors with the correct offsets.
10+
//
11+
// The MIT License (MIT)
12+
// Copyright (c) 2021 Josh Baker
13+
func StripJSONC(src []byte) []byte {
14+
return toJSON(src, nil)
15+
}
16+
17+
func toJSON(src, dst []byte) []byte {
18+
dst = dst[:0]
19+
for i := 0; i < len(src); i++ {
20+
if src[i] == '/' {
21+
if i < len(src)-1 {
22+
if src[i+1] == '/' {
23+
dst = append(dst, ' ', ' ')
24+
i += 2
25+
for ; i < len(src); i++ {
26+
if src[i] == '\n' {
27+
dst = append(dst, '\n')
28+
break
29+
} else if src[i] == '\t' || src[i] == '\r' {
30+
dst = append(dst, src[i])
31+
} else {
32+
dst = append(dst, ' ')
33+
}
34+
}
35+
continue
36+
}
37+
if src[i+1] == '*' {
38+
dst = append(dst, ' ', ' ')
39+
i += 2
40+
for ; i < len(src)-1; i++ {
41+
if src[i] == '*' && src[i+1] == '/' {
42+
dst = append(dst, ' ', ' ')
43+
i++
44+
break
45+
} else if src[i] == '\n' || src[i] == '\t' ||
46+
src[i] == '\r' {
47+
dst = append(dst, src[i])
48+
} else {
49+
dst = append(dst, ' ')
50+
}
51+
}
52+
continue
53+
}
54+
}
55+
}
56+
dst = append(dst, src[i])
57+
if src[i] == '"' {
58+
for i = i + 1; i < len(src); i++ {
59+
dst = append(dst, src[i])
60+
if src[i] == '"' {
61+
j := i - 1
62+
for ; ; j-- {
63+
if src[j] != '\\' {
64+
break
65+
}
66+
}
67+
if (j-i)%2 != 0 {
68+
break
69+
}
70+
}
71+
}
72+
} else if src[i] == '}' || src[i] == ']' {
73+
for j := len(dst) - 2; j >= 0; j-- {
74+
if dst[j] <= ' ' {
75+
continue
76+
}
77+
if dst[j] == ',' {
78+
dst[j] = ' '
79+
}
80+
break
81+
}
82+
}
83+
}
84+
return dst
85+
}

server/npm.go

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package server
22

33
import (
44
"archive/tar"
5+
"bytes"
56
"compress/gzip"
67
"encoding/base64"
78
"encoding/json"
@@ -18,6 +19,7 @@ import (
1819
"time"
1920

2021
"github.com/Masterminds/semver/v3"
22+
"github.com/esm-dev/esm.sh/server/common"
2123
"github.com/ije/gox/set"
2224
syncx "github.com/ije/gox/sync"
2325
"github.com/ije/gox/utils"
@@ -502,13 +504,52 @@ func (npmrc *NpmRC) installPackage(pkg Package) (packageJson *PackageJSON, err e
502504
err = ghInstall(installDir, pkg.Name, pkg.Version)
503505
// ensure 'package.json' file if not exists after installing from github
504506
if err == nil && !existsFile(packageJsonPath) {
505-
var f *os.File
506-
f, err = os.Create(packageJsonPath)
507+
buf := bytes.NewBuffer(nil)
508+
buf.WriteString(`{"name":"` + pkg.Name + `","version":"` + pkg.Version + `"`)
509+
var denoJson *PackageJSON
510+
if deonJsonPath := path.Join(installDir, "node_modules", pkg.Name, "deno.json"); existsFile(deonJsonPath) {
511+
var raw PackageJSONRaw
512+
if utils.ParseJSONFile(deonJsonPath, &raw) == nil {
513+
denoJson = raw.ToNpmPackage()
514+
}
515+
} else if deonJsoncPath := path.Join(installDir, "node_modules", pkg.Name, "deno.jsonc"); existsFile(deonJsoncPath) {
516+
data, err := os.ReadFile(deonJsoncPath)
517+
if err == nil {
518+
var raw PackageJSONRaw
519+
if json.Unmarshal(common.StripJSONC(data), &raw) == nil {
520+
denoJson = raw.ToNpmPackage()
521+
}
522+
}
523+
}
524+
if denoJson != nil {
525+
if len(denoJson.Imports) > 0 {
526+
buf.WriteString(`,"imports":{`)
527+
for k, v := range denoJson.Imports {
528+
if s, ok := v.(string); ok {
529+
buf.WriteString(`"` + k + `":"` + s + `",`)
530+
}
531+
}
532+
buf.Truncate(buf.Len() - 1)
533+
buf.WriteByte('}')
534+
}
535+
if denoJson.Exports.Len() > 0 {
536+
buf.WriteString(`,"exports":{`)
537+
for _, k := range denoJson.Exports.keys {
538+
if v, ok := denoJson.Exports.Get(k); ok {
539+
if s, ok := v.(string); ok {
540+
buf.WriteString(`"` + k + `":"` + s + `",`)
541+
}
542+
}
543+
}
544+
buf.Truncate(buf.Len() - 1)
545+
buf.WriteByte('}')
546+
}
547+
}
548+
buf.WriteByte('}')
549+
err = os.WriteFile(packageJsonPath, buf.Bytes(), 0644)
507550
if err != nil {
508551
return
509552
}
510-
defer f.Close()
511-
fmt.Fprintf(f, `{"name":"%s","version":"%s"}`, pkg.Name, pkg.Version)
512553
}
513554
} else if pkg.PkgPrNew {
514555
err = fetchPackageTarball(&NpmRegistry{}, installDir, pkg.Name, "https://pkg.pr.new/"+pkg.Name+"@"+pkg.Version)

test/gh/jsr.test.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { assertEquals } from "jsr:@std/assert";
2+
3+
import $ from "http://localhost:8080/gh/dsherret/dax";
4+
5+
Deno.test("jsr package from github", async () => {
6+
assertEquals(typeof $, "function");
7+
assertEquals(await $`echo 42`.text(), "42");
8+
});

0 commit comments

Comments
 (0)