a simpler package manager for no-build websites
0

Configure Feed

Select the types of activity you want to include in your feed.

unpm -> vendor

+42 -55
+2 -2
cmd/unpm/unpm.go
··· 11 11 12 12 "tangled.org/jakelazaroff.com/unpm/internal/cli" 13 13 "tangled.org/jakelazaroff.com/unpm/internal/inspect" 14 - "tangled.org/jakelazaroff.com/unpm/internal/unpm" 14 + "tangled.org/jakelazaroff.com/unpm/internal/vendor" 15 15 ) 16 16 17 17 func main() { 18 18 app := cli.App{ 19 - Vendor: unpm.Vendor, 19 + Vendor: vendor.Vendor, 20 20 Check: inspect.Check, 21 21 Why: inspect.Why, 22 22 }
-4
internal/cfg/cfg.go
··· 39 39 return nil, fmt.Errorf("parsing config: %w", err) 40 40 } 41 41 42 - if len(cfg.Imports) == 0 { 43 - return nil, fmt.Errorf("no imports found in %s", configPath) 44 - } 45 - 46 42 for key, url := range cfg.Imports { 47 43 if strings.HasSuffix(key, "/") { 48 44 return nil, fmt.Errorf("trailing-slash prefix entries are not supported: %q (list each subpath explicitly)", key)
-11
internal/cfg/cfg_test.go
··· 30 30 } 31 31 }) 32 32 33 - t.Run("no imports", func(t *testing.T) { 34 - dir := t.TempDir() 35 - p := filepath.Join(dir, "unpm.json") 36 - os.WriteFile(p, []byte(`{}`), 0o644) 37 - 38 - _, err := cfg.ReadConfig(p) 39 - if err == nil || !strings.Contains(err.Error(), "no imports") { 40 - t.Fatalf("expected 'no imports' error, got: %v", err) 41 - } 42 - }) 43 - 44 33 t.Run("trailing slash", func(t *testing.T) { 45 34 dir := t.TempDir() 46 35 p := filepath.Join(dir, "unpm.json")
+14 -12
internal/imports/imports.go
··· 4 4 // 5 5 // Copyright (c) 2026 Jake Lazaroff https://tangled.org/jakelazaroff.com/unpm 6 6 7 - // Package imports scans JavaScript and TypeScript source for the references 8 - // unpm cares about — import/export specifiers, type-only imports, and source 9 - // map comments — and rewrites them. It is the single home for "what counts as 10 - // an import," shared by the vendoring engine and the check/why inspector. 7 + // Package imports scans for and rewrites imports in JavaScript and TypeScript 11 8 package imports 12 9 13 10 import ( ··· 17 14 "strings" 18 15 ) 19 16 20 - // staticImportRe matches a static import/export statement whose specifier is a 21 - // string literal: prefix(1) quote(2) spec(3) quote(4). 17 + // staticImportRe matches a static import/export statement whose specifier is a string literal 18 + // prefix(1) quote(2) spec(3) quote(4) 22 19 var staticImportRe = regexp.MustCompile(`(\b(?:import|export)\s*(?:[^"']*\bfrom\s*|))(["'])([^"']+)(["'])`) 23 20 24 - // dynamicImportRe matches a dynamic import() expression whose specifier is a 25 - // string literal: prefix(1) quote(2) spec(3) quote(4). 21 + // dynamicImportRe matches a dynamic import() expression whose specifier is a string literal 22 + // prefix(1) quote(2) spec(3) quote(4). 26 23 var dynamicImportRe = regexp.MustCompile(`(\bimport\s*\(\s*)(["'])([^"']+)(["'][^)]*\))`) 27 24 28 - // sourceMappingRe matches //# sourceMappingURL=... comments: prefix(1) url(2). 25 + // sourceMappingRe matches //# sourceMappingURL=... comments: 26 + // prefix(1) url(2) 29 27 var sourceMappingRe = regexp.MustCompile(`(//[#@]\s*sourceMappingURL\s*=\s*)(\S+)`) 30 28 31 29 // Scan returns every import/export/dynamic-import specifier in src, in source 32 30 // order, including duplicates and bare specifiers. 33 31 func Scan(src string) []string { 34 32 type entry struct { 35 - pos int 36 - spec string 33 + pos int 34 + specifier string 37 35 } 36 + 38 37 var entries []entry 39 38 for _, loc := range staticImportRe.FindAllStringSubmatchIndex(src, -1) { 40 39 entries = append(entries, entry{loc[0], src[loc[6]:loc[7]]}) 41 40 } 41 + 42 42 for _, loc := range dynamicImportRe.FindAllStringSubmatchIndex(src, -1) { 43 43 entries = append(entries, entry{loc[0], src[loc[6]:loc[7]]}) 44 44 } 45 + 45 46 sort.Slice(entries, func(i, j int) bool { return entries[i].pos < entries[j].pos }) 46 47 specs := make([]string, len(entries)) 47 48 for i, e := range entries { 48 - specs[i] = e.spec 49 + specs[i] = e.specifier 49 50 } 51 + 50 52 return specs 51 53 } 52 54
+1 -1
internal/unpm/unpm.go internal/vendor/vendor.go
··· 4 4 // 5 5 // Copyright (c) 2026 Jake Lazaroff https://tangled.org/jakelazaroff.com/unpm 6 6 7 - package unpm 7 + package vendor 8 8 9 9 import ( 10 10 "encoding/json"
+25 -25
internal/unpm/unpm_test.go internal/vendor/vendor_test.go
··· 4 4 // 5 5 // Copyright (c) 2026 Jake Lazaroff https://tangled.org/jakelazaroff.com/unpm 6 6 7 - package unpm_test 7 + package vendor_test 8 8 9 9 import ( 10 10 "encoding/json" ··· 16 16 "testing" 17 17 18 18 "tangled.org/jakelazaroff.com/unpm/internal/cfg" 19 - "tangled.org/jakelazaroff.com/unpm/internal/unpm" 19 + "tangled.org/jakelazaroff.com/unpm/internal/vendor" 20 20 ) 21 21 22 22 // testFile defines a file served by the test server. ··· 52 52 Imports: map[string]string{"mylib": srv.URL + "/mylib.js"}, 53 53 Unpm: cfg.Options{Out: outDir, Root: "/"}, 54 54 } 55 - if _, err := unpm.Vendor(c); err != nil { 55 + if _, err := vendor.Vendor(c); err != nil { 56 56 t.Fatal(err) 57 57 } 58 58 ··· 105 105 Imports: map[string]string{"a": srv.URL + "/a.js"}, 106 106 Unpm: cfg.Options{Out: outDir, Root: "/"}, 107 107 } 108 - if _, err := unpm.Vendor(c); err != nil { 108 + if _, err := vendor.Vendor(c); err != nil { 109 109 t.Fatal(err) 110 110 } 111 111 ··· 138 138 Imports: map[string]string{"entry": srv.URL + "/entry.js"}, 139 139 Unpm: cfg.Options{Out: outDir, Root: "/"}, 140 140 } 141 - if _, err := unpm.Vendor(c); err != nil { 141 + if _, err := vendor.Vendor(c); err != nil { 142 142 t.Fatal(err) 143 143 } 144 144 ··· 184 184 Imports: map[string]string{"lib": srv.URL + "/lib.d.ts"}, 185 185 Unpm: cfg.Options{Out: outDir, Root: "/"}, 186 186 } 187 - if _, err := unpm.Vendor(c); err != nil { 187 + if _, err := vendor.Vendor(c); err != nil { 188 188 t.Fatal(err) 189 189 } 190 190 ··· 222 222 Imports: map[string]string{"preact": srv.URL + "/preact"}, 223 223 Unpm: cfg.Options{Out: outDir, Root: "/"}, 224 224 } 225 - if _, err := unpm.Vendor(c); err != nil { 225 + if _, err := vendor.Vendor(c); err != nil { 226 226 t.Fatal(err) 227 227 } 228 228 ··· 262 262 Imports: map[string]string{"mylib": srv.URL + "/mylib.js"}, 263 263 Unpm: cfg.Options{Out: outDir, Root: "/"}, 264 264 } 265 - if _, err := unpm.Vendor(c); err != nil { 265 + if _, err := vendor.Vendor(c); err != nil { 266 266 t.Fatal(err) 267 267 } 268 268 ··· 307 307 Imports: map[string]string{"preact": srv.URL + "/preact@10"}, 308 308 Unpm: cfg.Options{Out: outDir, Root: "/"}, 309 309 } 310 - if _, err := unpm.Vendor(c); err != nil { 310 + if _, err := vendor.Vendor(c); err != nil { 311 311 t.Fatal(err) 312 312 } 313 313 ··· 342 342 Imports: map[string]string{"mylib": srv.URL + "/mylib.js"}, 343 343 Unpm: cfg.Options{Out: outDir, Root: "/assets/vendor"}, 344 344 } 345 - if _, err := unpm.Vendor(c); err != nil { 345 + if _, err := vendor.Vendor(c); err != nil { 346 346 t.Fatal(err) 347 347 } 348 348 ··· 376 376 Imports: map[string]string{"app": srv.URL + "/app.js"}, 377 377 Unpm: cfg.Options{Out: outDir, Root: "/"}, 378 378 } 379 - if _, err := unpm.Vendor(c); err != nil { 379 + if _, err := vendor.Vendor(c); err != nil { 380 380 t.Fatal(err) 381 381 } 382 382 ··· 408 408 Imports: map[string]string{"entry": srv.URL + "/entry.js"}, 409 409 Unpm: cfg.Options{Out: outDir, Root: "/"}, 410 410 } 411 - if _, err := unpm.Vendor(c); err != nil { 411 + if _, err := vendor.Vendor(c); err != nil { 412 412 t.Fatal(err) 413 413 } 414 414 ··· 444 444 Imports: map[string]string{"entry": srv.URL + "/entry.js"}, 445 445 Unpm: cfg.Options{Out: outDir, Root: "/"}, 446 446 } 447 - if _, err := unpm.Vendor(c); err != nil { 447 + if _, err := vendor.Vendor(c); err != nil { 448 448 t.Fatal(err) 449 449 } 450 450 ··· 486 486 }, 487 487 Unpm: cfg.Options{Out: outDir, Root: "/", Pin: []string{host + "/b.js"}}, 488 488 } 489 - if _, err := unpm.Vendor(c); err != nil { 489 + if _, err := vendor.Vendor(c); err != nil { 490 490 t.Fatal(err) 491 491 } 492 492 ··· 525 525 }, 526 526 Unpm: cfg.Options{Out: outDir, Root: "/", Pin: []string{host + "/**"}}, 527 527 } 528 - if _, err := unpm.Vendor(c); err != nil { 528 + if _, err := vendor.Vendor(c); err != nil { 529 529 t.Fatal(err) 530 530 } 531 531 ··· 556 556 Imports: map[string]string{"htm": srv.URL + "/src/index.mjs"}, 557 557 Unpm: cfg.Options{Out: outDir, Root: "/"}, 558 558 } 559 - if _, err := unpm.Vendor(c); err != nil { 559 + if _, err := vendor.Vendor(c); err != nil { 560 560 t.Fatal(err) 561 561 } 562 562 ··· 598 598 Imports: map[string]string{"lib": srv.URL + "/entry.js"}, 599 599 Unpm: cfg.Options{Out: outDir, Root: "/"}, 600 600 } 601 - if _, err := unpm.Vendor(c); err != nil { 601 + if _, err := vendor.Vendor(c); err != nil { 602 602 t.Fatal(err) 603 603 } 604 604 ··· 635 635 Imports: map[string]string{"lib": srv.URL + "/index.ts"}, 636 636 Unpm: cfg.Options{Out: outDir, Root: "/"}, 637 637 } 638 - if _, err := unpm.Vendor(c); err != nil { 638 + if _, err := vendor.Vendor(c); err != nil { 639 639 t.Fatal(err) 640 640 } 641 641 ··· 698 698 }, 699 699 Unpm: cfg.Options{Out: outDir, Root: "/"}, 700 700 } 701 - warnings, err := unpm.Vendor(c) 701 + warnings, err := vendor.Vendor(c) 702 702 if err != nil { 703 703 t.Fatalf("vendor should not fail when one entry returns HTML: %v", err) 704 704 } ··· 743 743 Imports: map[string]string{"entry": srv.URL + "/entry.ts"}, 744 744 Unpm: cfg.Options{Out: outDir, Root: "/"}, 745 745 } 746 - if _, err := unpm.Vendor(c); err != nil { 746 + if _, err := vendor.Vendor(c); err != nil { 747 747 t.Fatal(err) 748 748 } 749 749 ··· 780 780 Imports: map[string]string{"mylib": srv.URL + "/mylib.js"}, 781 781 Unpm: cfg.Options{Out: outDir, Root: "/"}, 782 782 } 783 - if _, err := unpm.Vendor(c); err != nil { 783 + if _, err := vendor.Vendor(c); err != nil { 784 784 t.Fatal(err) 785 785 } 786 786 ··· 811 811 Imports: map[string]string{"mylib": srv.URL + "/mylib.js"}, 812 812 Unpm: cfg.Options{Out: outDir, Root: "/"}, 813 813 } 814 - if _, err := unpm.Vendor(c); err != nil { 814 + if _, err := vendor.Vendor(c); err != nil { 815 815 t.Fatalf("vendor should succeed even when sidecar is absent: %v", err) 816 816 } 817 817 ··· 840 840 Types: map[string]string{"mylib": srv.URL + "/explicit-types.d.ts"}, 841 841 Unpm: cfg.Options{Out: outDir, Root: "/"}, 842 842 } 843 - if _, err := unpm.Vendor(c); err != nil { 843 + if _, err := vendor.Vendor(c); err != nil { 844 844 t.Fatal(err) 845 845 } 846 846 ··· 878 878 Types: map[string]string{"mylib": srv.URL + "/types.d.ts"}, 879 879 Unpm: cfg.Options{Out: outDir, Root: "/"}, 880 880 } 881 - if _, err := unpm.Vendor(c); err != nil { 881 + if _, err := vendor.Vendor(c); err != nil { 882 882 t.Fatal(err) 883 883 } 884 884 ··· 917 917 Imports: map[string]string{"a": srv.URL + "/a.js"}, 918 918 Unpm: cfg.Options{Out: outDir, Root: "/"}, 919 919 } 920 - if _, err := unpm.Vendor(c); err != nil { 920 + if _, err := vendor.Vendor(c); err != nil { 921 921 t.Fatal(err) 922 922 } 923 923