Created
December 13, 2024 13:44
-
-
Save barelyhuman/478ea13409de454ebfd37cb8106fe9ae to your computer and use it in GitHub Desktop.
[For reference] simpler npm registry package resolver and downloader
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"archive/tar" | |
"bytes" | |
"compress/gzip" | |
"encoding/json" | |
"fmt" | |
"io" | |
"log" | |
"net/http" | |
"net/url" | |
"os" | |
"path/filepath" | |
"strings" | |
"sync" | |
"time" | |
) | |
const registry = "https://registry.npmjs.com/" | |
const nodeModulesDir = "node_modules" | |
type PackageJSON struct { | |
Name string `json:"name"` | |
DevDependencies DepDefinition `json:"devDependencies"` | |
Dependencies DepDefinition `json:"dependencies"` | |
} | |
type PackageMeta struct { | |
Name string `json:"name"` | |
Versions map[string]Version | |
DistTags map[string]string `json:"dist-tags"` | |
} | |
type DepDefinition map[string]string | |
type DistDef struct { | |
Tarball string `json:"tarball"` | |
} | |
type Version struct { | |
Name string `json:"name"` | |
DevDependencies DepDefinition `json:"devDependencies"` | |
Dependencies DepDefinition `json:"dependencies"` | |
Dist DistDef `json:"dist"` | |
} | |
func main() { | |
start := time.Now() | |
done := make(chan bool) | |
basePkg, _ := readPackageJSON("package.json") | |
resolved := map[string]bool{} | |
os.MkdirAll(nodeModulesDir, os.ModePerm) | |
go func() { | |
resolveDeps(basePkg.Dependencies, resolved, "node_modules") | |
done <- true | |
}() | |
go func() { | |
resolveDeps(basePkg.DevDependencies, resolved, "node_modules") | |
done <- true | |
}() | |
<-done | |
<-done | |
fmt.Println("Downloaded\n\n") | |
updateTerminalLog("\nFinished in %v\n", time.Since(start)) | |
} | |
func getPkgMeta(pkgName string) Version { | |
pkgPath, _ := url.JoinPath(registry, pkgName) | |
resp, err := http.Get(pkgPath) | |
if err != nil { | |
log.Printf("failed to download %v", pkgPath) | |
return Version{} | |
} | |
defer resp.Body.Close() | |
buf, _ := io.ReadAll(resp.Body) | |
pm := PackageMeta{} | |
if err = json.Unmarshal(buf, &pm); err != nil { | |
panic(err) | |
} | |
latestTag := pm.DistTags["latest"] | |
versionInfo := pm.Versions[latestTag] | |
return versionInfo | |
} | |
func resolveDeps(deps DepDefinition, resolved map[string]bool, parentPath string) { | |
var wg sync.WaitGroup | |
for depName := range deps { | |
if resolved[depName] { | |
continue | |
} | |
wg.Add(2) | |
pkg := getPkgMeta(depName) | |
go func() { | |
resolved[depName] = true | |
resolveDeps(pkg.Dependencies, resolved, filepath.Join(parentPath, depName, "node_modules")) | |
wg.Done() | |
}() | |
go func() { | |
downloadPkg(depName, pkg.Dist.Tarball, filepath.Join(parentPath, depName)) | |
wg.Done() | |
}() | |
} | |
wg.Wait() | |
} | |
func downloadPkg(pkgName string, link string, downloadTo string) { | |
updateTerminalLog("Downloading %v", pkgName) | |
baseOutPath := filepath.Join(downloadTo) | |
tarFilePath := filepath.Join(pkgName + ".tar.gz") | |
os.MkdirAll(baseOutPath, os.ModePerm) | |
resp, err := http.Get(link) | |
if err != nil { | |
return | |
} | |
defer resp.Body.Close() | |
memBuffer := bytes.Buffer{} | |
defer memBuffer.Reset() | |
zr, err := gzip.NewReader(resp.Body) | |
if err != nil { | |
log.Fatal(err) | |
} | |
defer zr.Close() | |
// Just output the data for the example. | |
if _, err := io.Copy(&memBuffer, zr); err != nil { | |
log.Fatal(err) | |
} | |
tr := tar.NewReader(&memBuffer) | |
for { | |
hdr, err := tr.Next() | |
if err == io.EOF { | |
break // End of archive | |
} | |
if err != nil { | |
log.Fatal(err) | |
} | |
outfilePath := filepath.Join(baseOutPath, strings.Replace(hdr.Name, "package/", "./", 1)) | |
outFileDir := filepath.Dir(outfilePath) | |
os.MkdirAll(outFileDir, os.ModePerm) | |
out, _ := os.Create(outfilePath) | |
defer out.Close() | |
if _, err := io.Copy(out, tr); err != nil { | |
continue | |
} | |
} | |
os.Remove(tarFilePath) | |
updateTerminalLog("Downloading %v ... done", pkgName) | |
} | |
func readPackageJSON(path string) (result PackageJSON, err error) { | |
fileData, err := os.ReadFile(path) | |
if err != nil { | |
return | |
} | |
if err = json.Unmarshal(fileData, &result); err != nil { | |
return | |
} | |
return | |
} | |
func updateTerminalLog(msg string, interfaces ...any) { | |
fmt.Printf("\033[1A\033[K") | |
fmt.Printf(msg+"\n", interfaces...) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment