Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion cmd/cue/cmd/modget.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (

"cuelang.org/go/internal/mod/modload"
"cuelang.org/go/mod/modfile"
"cuelang.org/go/mod/module"
)

func newModGetCmd(c *Command) *cobra.Command {
Expand Down Expand Up @@ -69,7 +70,7 @@ func runModGet(cmd *Command, args []string) error {
if err != nil {
return err
}
mf, err := modload.UpdateVersions(ctx, os.DirFS(modRoot), ".", reg, args)
mf, err := modload.UpdateVersions(ctx, module.OSDirFS(modRoot), ".", reg, args)
if err != nil {
return suggestModCommand(err)
}
Expand Down
5 changes: 3 additions & 2 deletions cmd/cue/cmd/modtidy.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (

"cuelang.org/go/internal/mod/modload"
"cuelang.org/go/mod/modfile"
"cuelang.org/go/mod/module"
)

func newModTidyCmd(c *Command) *cobra.Command {
Expand Down Expand Up @@ -62,10 +63,10 @@ func runModTidy(cmd *Command, args []string) error {
return err
}
if flagCheck.Bool(cmd) {
err := modload.CheckTidy(ctx, os.DirFS(modRoot), ".", reg)
err := modload.CheckTidy(ctx, module.OSDirFS(modRoot), ".", reg)
return suggestModCommand(err)
}
mf, err := modload.Tidy(ctx, os.DirFS(modRoot), ".", reg)
mf, err := modload.Tidy(ctx, module.OSDirFS(modRoot), ".", reg)
if err != nil {
return suggestModCommand(err)
}
Expand Down
60 changes: 60 additions & 0 deletions cmd/cue/cmd/testdata/script/modreplace_local.txtar
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Test local path replace directives

# The main module references a dependency that is replaced with a local path.
# The local-dep directory contains the replacement module.

# Test that cue mod tidy works with local replacements
exec cue mod tidy
cmp cue.mod/module.cue want-module.cue

# Test that cue eval uses the local replacement
exec cue eval .
cmp stdout want-eval.txt

# Test that cue export also works
exec cue export .
stdout '"from local replacement"'

# Test that cue vet works (should pass validation)
exec cue vet .

# Test that cue def works (show definitions)
exec cue def .
stdout 'output:'

-- cue.mod/module.cue --
module: "example.com/main@v0"
language: version: "v0.9.0"

deps: {
"example.com/dep@v0": {
v: "v0.1.0"
replace: "./local-dep"
}
}
-- main.cue --
package main

import "example.com/dep@v0:lib"

output: lib.value
-- local-dep/cue.mod/module.cue --
module: "example.com/dep@v0"
language: version: "v0.9.0"
-- local-dep/lib.cue --
package lib

value: "from local replacement"
-- want-eval.txt --
output: "from local replacement"
-- want-module.cue --
module: "example.com/main@v0"
language: {
version: "v0.9.0"
}
deps: {
"example.com/dep@v0": {
v: "v0.1.0"
replace: "./local-dep"
}
}
22 changes: 22 additions & 0 deletions cmd/cue/cmd/testdata/script/modreplace_local_missing.txtar
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Test error handling when local replacement directory does not exist

# cue mod tidy should fail when the replacement directory is missing
! exec cue mod tidy
stderr 'does not exist'

-- cue.mod/module.cue --
module: "example.com/main@v0"
language: version: "v0.9.0"

deps: {
"example.com/missing@v0": {
v: "v0.1.0"
replace: "./nonexistent-dir"
}
}
-- main.cue --
package main

import "example.com/missing@v0:lib"

output: lib.value
55 changes: 55 additions & 0 deletions cmd/cue/cmd/testdata/script/modreplace_local_sibling.txtar
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Test local path replace directives with sibling directory (../)

# Create the sibling directory structure
mkdir sibling-dep
mkdir sibling-dep/cue.mod
cp sibling-module.cue sibling-dep/cue.mod/module.cue
cp sibling-lib.cue sibling-dep/lib.cue

# Run from the main directory
cd main

# Test that cue mod tidy works with sibling path replacements
exec cue mod tidy
cmp cue.mod/module.cue want-module.cue

# Test that cue eval uses the sibling replacement
exec cue eval .
cmp stdout want-eval.txt

-- sibling-module.cue --
module: "example.com/sibling@v0"
language: version: "v0.9.0"
-- sibling-lib.cue --
package lib

value: "from sibling directory"
-- main/cue.mod/module.cue --
module: "example.com/main@v0"
language: version: "v0.9.0"

deps: {
"example.com/sibling@v0": {
v: "v0.1.0"
replace: "../sibling-dep"
}
}
-- main/main.cue --
package main

import "example.com/sibling@v0:lib"

output: lib.value
-- main/want-eval.txt --
output: "from sibling directory"
-- main/want-module.cue --
module: "example.com/main@v0"
language: {
version: "v0.9.0"
}
deps: {
"example.com/sibling@v0": {
v: "v0.1.0"
replace: "../sibling-dep"
}
}
71 changes: 71 additions & 0 deletions cmd/cue/cmd/testdata/script/modreplace_preserve_orphan.txtar
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Test that replace directives are preserved when dependency is removed
#
# This tests that replace directives are intentionally preserved during tidy
# even when the dependency is no longer used (matching Go's go mod tidy behavior).
# This allows users to maintain replacements for dependencies that may be
# re-added later without losing the configuration.

# Initial state: main.cue imports dep, module.cue has replace directive
exec cue mod tidy
exec cue eval .
stdout '"from local replacement"'

# Remove the import from main.cue
cp _templates/main_no_import.cue main.cue

# Run tidy - the replace directive should be preserved even though dep is unused
exec cue mod tidy
cmp cue.mod/module.cue _golden/want-module-after-tidy.cue

# Re-add the import
cp _templates/main_with_import.cue main.cue

# Verify the replacement still works after tidy
exec cue mod tidy
exec cue eval .
stdout '"from local replacement"'

-- cue.mod/module.cue --
module: "example.com/main@v0"
language: version: "v0.9.0"

deps: {
"example.com/dep@v0": {
v: "v0.1.0"
replace: "./local-dep"
}
}
-- main.cue --
package main

import "example.com/dep@v0:lib"

output: lib.value
-- _templates/main_with_import.cue --
package main

import "example.com/dep@v0:lib"

output: lib.value
-- _templates/main_no_import.cue --
package main

output: "no dependency"
-- local-dep/cue.mod/module.cue --
module: "example.com/dep@v0"
language: version: "v0.9.0"
-- local-dep/lib.cue --
package lib

value: "from local replacement"
-- _golden/want-module-after-tidy.cue --
module: "example.com/main@v0"
language: {
version: "v0.9.0"
}
deps: {
"example.com/dep@v0": {
v: "v0.1.0"
replace: "./local-dep"
}
}
54 changes: 54 additions & 0 deletions cmd/cue/cmd/testdata/script/modreplace_remote.txtar
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Test remote module replace directives
# A module can be replaced with a different module from the registry.

# Test that cue mod tidy works with remote replacements
exec cue mod tidy
cmp cue.mod/module.cue want-module.cue

# Test that cue eval uses the replacement module
exec cue eval .
cmp stdout want-eval.txt

-- cue.mod/module.cue --
module: "example.com/main@v0"
language: version: "v0.9.0"

deps: {
"example.com/original@v0": {
v: "v0.1.0"
replace: "example.com/replacement@v0.1.0"
}
}
-- main.cue --
package main

import "example.com/original@v0:lib"

output: lib.value
-- _registry/example.com_original_v0.1.0/cue.mod/module.cue --
module: "example.com/original@v0"
language: version: "v0.9.0"
-- _registry/example.com_original_v0.1.0/lib.cue --
package lib

value: "from original (should not see this)"
-- _registry/example.com_replacement_v0.1.0/cue.mod/module.cue --
module: "example.com/replacement@v0"
language: version: "v0.9.0"
-- _registry/example.com_replacement_v0.1.0/lib.cue --
package lib

value: "from replacement module"
-- want-eval.txt --
output: "from replacement module"
-- want-module.cue --
module: "example.com/main@v0"
language: {
version: "v0.9.0"
}
deps: {
"example.com/original@v0": {
v: "v0.1.0"
replace: "example.com/replacement@v0.1.0"
}
}
22 changes: 16 additions & 6 deletions cue/load/instances.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,10 @@ func loadAbsPackage(
ip := ast.ParseImportPath(pkg)
ip.Version = semver.Major(mv.Version())

pkgs := loadPackages(ctx, cfg, mf, loc, []string{ip.String()}, tg)
pkgs, err := loadPackages(ctx, cfg, mf, loc, []string{ip.String()}, tg)
if err != nil {
return "", nil, err
}
return ip.String(), pkgs, nil
}

Expand Down Expand Up @@ -268,7 +271,7 @@ func loadPackagesFromArgs(
},
slices.Sorted(maps.Keys(pkgPaths)),
tg,
), nil
)
}

func loadPackages(
Expand All @@ -278,20 +281,27 @@ func loadPackages(
mainModLoc module.SourceLoc,
pkgPaths []string,
tg *tagger,
) *modpkgload.Packages {
) (*modpkgload.Packages, error) {
mainModPath := mainMod.QualifiedModule()
// Wrap the registry to handle local path replacements.
// This allows cue eval/export to read requirements from local modules.
reg, err := modload.NewLocalReplacementRegistry(cfg.Registry, mainModLoc, mainMod.Replacements())
if err != nil {
return nil, err
}
reqs := modrequirements.NewRequirements(
mainModPath,
cfg.Registry,
reg,
mainMod.DepVersions(),
mainMod.DefaultMajorVersions(),
mainMod.Replacements(),
)
return modpkgload.LoadPackages(
ctx,
mainModPath,
mainModLoc,
reqs,
cfg.Registry,
reg,
pkgPaths,
func(pkgPath string, mod module.Version, fsys fs.FS, mf modimports.ModuleFile) bool {
if !cfg.Tools && strings.HasSuffix(mf.FilePath, "_tool.cue") {
Expand Down Expand Up @@ -322,7 +332,7 @@ func loadPackages(
}
return true
},
)
), nil
}

func isAbsVersionPackage(p string) bool {
Expand Down
2 changes: 1 addition & 1 deletion cue/load/loader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ module: conflicting values 123 and "" (mismatched types int and string):
module: conflicting values 123 and string (mismatched types int and string):
$CWD/testdata/badmod/cue.mod/module.cue:2:9
cuelang.org/go/mod/modfile/schema.cue:56:12
cuelang.org/go/mod/modfile/schema.cue:98:12
cuelang.org/go/mod/modfile/schema.cue:104:12
path: ""
module: ""
root: ""
Expand Down
6 changes: 2 additions & 4 deletions internal/lsp/cache/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ package cache

import (
"cuelang.org/go/internal/lsp/fscache"
"cuelang.org/go/internal/mod/modpkgload"
"cuelang.org/go/internal/mod/modrequirements"
"cuelang.org/go/internal/mod/modload"
"cuelang.org/go/mod/modconfig"
)

Expand Down Expand Up @@ -42,6 +41,5 @@ type Cache struct {
}

type Registry interface {
modrequirements.Registry
modpkgload.Registry
modload.Registry
}
Loading