Skip to content

feat: support workspace subdirectory resolution with WithPathBase and WithPackageDeps#39

Merged
bennypowers merged 2 commits into
mainfrom
feat/relative-root
May 6, 2026
Merged

feat: support workspace subdirectory resolution with WithPathBase and WithPackageDeps#39
bennypowers merged 2 commits into
mainfrom
feat/relative-root

Conversation

@bennypowers

@bennypowers bennypowers commented May 6, 2026

Copy link
Copy Markdown
Owner

Summary

Closes #38.

  • Add WithPathBase(dir) option to rebase workspace package paths relative to a given directory instead of the resolution root. Paths under node_modules/ are unaffected. This moves the rebaseForServeRoot workaround from cem into mappa.
  • Add WithPackageDeps(dir) option to limit dependency collection to only the dependencies listed in the package.json at the given directory, avoiding warnings for irrelevant workspace packages.
  • Both options are exposed via Go builder methods, CLI flags (--path-base, --package-deps), WASM/JS options, and the Options struct.
  • Relative paths for both options are normalized to absolute paths relative to rootDir.
  • Fix a pre-existing race condition in ResolveIncremental where workspace package writes were unprotected by the mutex.

Test plan

  • TestResolverWithPathBase -- workspace paths rebased, node_modules unchanged, scope keys preserved
  • TestResolverWithPathBaseSameAsRoot -- no-op when path base equals resolution root
  • TestResolverWithPathBaseAutoDiscovery -- works with auto-discovered workspaces
  • TestResolverWithPathBaseOutsideRoot -- no-op when path base is outside root
  • TestResolverWithRelativePathBase -- relative path normalized correctly
  • TestResolverWithPathBaseIncrementalIdempotent -- rebase is idempotent across incremental updates
  • TestResolverWithPackageDeps -- only scoped package's deps resolved
  • TestResolverWithPackageDepsFallback -- all deps when packageDeps unset
  • TestResolverWithPackageDepsError -- error returned for nonexistent path
  • TestResolverWithRelativePackageDeps -- relative path normalized correctly
  • TestResolverWithPathBaseAndPackageDeps -- combined usage
  • TestResolverBuilderPropagation -- all fields survive builder method chaining
  • TestOptionsApplyPathBaseAndPackageDeps -- Options.Apply wiring
  • 351 tests pass, zero lint issues, zero race conditions

Assisted-By: Claude Opus 4.6 (1M context) noreply@anthropic.com

Summary by CodeRabbit

Release Notes

  • New Features

    • Added path-base configuration option for rebasing workspace paths during import-map generation
    • Added package-deps option to limit dependency resolution to a specific package's dependencies
    • Extended CLI and API interfaces to support both new configuration parameters
  • Tests

    • Expanded test coverage for path-base rebasing, workspace behavior, and package-scoped dependency resolution

@coderabbitai

coderabbitai Bot commented May 6, 2026

Copy link
Copy Markdown

Note

Reviews paused

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Warning

Rate limit exceeded

@bennypowers has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 44 minutes and 44 seconds before requesting another review.

To continue reviewing without waiting, purchase usage credits in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4d6adf88-eecd-444d-93b8-27a6992bac47

📥 Commits

Reviewing files that changed from the base of the PR and between 4020d05 and b06c49e.

📒 Files selected for processing (1)
  • resolve/local/local.go
📝 Walkthrough

Walkthrough

This PR adds two new configuration options to the local resolver: PathBase (for rebasing workspace paths relative to a serve root) and PackageDeps (for limiting dependency resolution to a specific package's dependencies). The changes wire these options through the CLI, WASM, and JavaScript APIs, extend the resolver's core logic to apply path normalization and import-map rebasing, and include comprehensive test coverage with new workspace fixtures.

Changes

Path Base and Package Dependencies Scoping

Layer / File(s) Summary
Type Definitions
resolve/local/options.go, npm/src/mappa.ts
New PathBase and PackageDeps fields added to Options struct and ResolveOptions interface to define path rebasing and dependency scoping configuration.
Core Resolver Fields & Builders
resolve/local/local.go (lines 42–46, 226–261)
Resolver struct extended with pathBase and packageDeps fields. New public methods WithPathBase(dir string) and WithPackageDeps(dir string) added to configure these behaviors. All existing builder methods (WithPackages, WithExclude, WithTemplate, etc.) updated to propagate new fields.
Normalization & Rebasing Helpers
resolve/local/local.go (lines 264–278, 1097–1133)
New helper normalizePathOptions() resolves pathBase and packageDeps relative to root directory. New helpers rebaseImportMapForPathBase() and rebasePath() apply path rebasing to import-map entries and individual paths.
Resolution Logic Integration
resolve/local/local.go (lines 434–435, 571–613, 936–937, 1041–1043)
resolveInternal() and ResolveIncremental() call normalizePathOptions() at start. Workspace dependency collection reads from packageDeps package.json when set, otherwise gathers all workspace packages. Import map is rebased for pathBase before merging results.
Options Apply Wiring
resolve/local/options.go (lines 61–66)
Options.Apply() method updated to invoke WithPathBase() and WithPackageDeps() when fields are provided, connecting configuration to resolver instance.
CLI & WASM Integration
cmd/generate/generate.go (lines 71–72, 82–83, 123–124), wasm/main.go (lines 232–236, 246–247, 282–287)
New flags path-base and package-deps added to generate command and bound to viper. WASM resolveOptions struct extended with corresponding fields; resolved from input object and passed to local.Options.
Tests & Testdata
resolve/local/local_test.go (lines 21–24, 377–727), testdata/workspace-*/...
Thirteen new test functions validate path-base rebasing, auto-discovery, package-deps filtering, builder chain propagation, relative path handling, incremental resolution, and combined behavior. New test fixtures include workspace structures with packages and expected import maps for rebased and non-rebased scenarios.

Sequence Diagram

sequenceDiagram
    actor Caller
    participant CLI/WASM as CLI or WASM
    participant Resolver as Local Resolver
    participant FileSystem as FileSystem<br/>& PackageJSON
    participant ImportMap as ImportMap

    Caller->>CLI/WASM: Call with --path-base /examples/kitchen-sink --package-deps /path/to/package
    CLI/WASM->>Resolver: Create resolver with WithPathBase() and WithPackageDeps()
    activate Resolver
    Resolver->>Resolver: Store pathBase and packageDeps
    deactivate Resolver
    Caller->>Resolver: Resolve(workspaceRoot)
    activate Resolver
    Resolver->>Resolver: normalizePathOptions() — resolve pathBase/packageDeps relative to root
    Resolver->>FileSystem: Read packages from workspace
    alt packageDeps is set
        Resolver->>FileSystem: Read specific package.json at packageDeps path
        FileSystem-->>Resolver: Dependencies from targeted package
        Resolver->>Resolver: Gather only those dependencies
    else packageDeps not set
        Resolver->>FileSystem: Read all workspace packages
        FileSystem-->>Resolver: All dependencies
        Resolver->>Resolver: Gather all workspace dependencies
    end
    Resolver->>FileSystem: Resolve node_modules and graph relationships
    FileSystem-->>Resolver: Computed imports and scopes
    alt pathBase is set and differs from root
        Resolver->>ImportMap: rebaseImportMapForPathBase() — rebase paths relative to pathBase
        ImportMap-->>Resolver: Rebased import map
    end
    Resolver-->>Caller: Return result with rebased/filtered imports
    deactivate Resolver
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • bennypowers/mappa#33: Extends the same local resolver and WASM/JavaScript API surface by adding pathBase and packageDeps options through local.Options, wasm main.go, and npm ResolveOptions.
  • bennypowers/mappa#13: Both PRs modify local resolver workspace discovery and dependency handling in resolve/local/local.go.
  • bennypowers/mappa#26: Both PRs modify wasm/main.go and extend the resolve options surface with new fields propagated from WASM input.

Poem

🐰 With whiskers twitching, paths now bend,
A serve root shift, workspace blend!
No more rebase dance, no more fuss,
Each package gets just what it must! 🌱✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: adding WithPathBase and WithPackageDeps features to support workspace subdirectory resolution.
Linked Issues check ✅ Passed All requirements from issue #38 are met: WithPathBase/WithServeRoot option rebases workspace paths, WithPackageDeps restricts resolution to specific package dependencies, APIs exposed across CLI/WASM/Go, race condition fixed, comprehensive tests added.
Out of Scope Changes check ✅ Passed All changes directly support the core objectives: new CLI flags, Options fields, Resolver methods, test data for path rebasing and package-deps scoping, and WASM integration are all in scope.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/relative-root

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

github-actions Bot commented May 6, 2026

Copy link
Copy Markdown

Build Artifacts

OS x64 arm64
Linux mappa-linux-x64 mappa-linux-arm64
macOS mappa-darwin-x64 mappa-darwin-arm64
Windows mappa-win32-x64 mappa-win32-arm64

Built from 455c9ae @ feat/relative-root

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
resolve/local/local.go (1)

578-596: 💤 Low value

Drop the unnecessary inner block.

The bare { ... } only scopes ownerName for the lookup. Inlining it (or extracting a small helper for owner attribution) reads more naturally and matches the style of the else branch.

♻️ Proposed flattening
 	if r.packageDeps != "" {
 		pkgJSON, err := r.parsePackageJSON(filepath.Join(r.packageDeps, "package.json"))
 		if err != nil {
 			return nil, graph, fmt.Errorf("package-deps %s: %w", r.packageDeps, err)
 		}
-		{
-			var ownerName string
-			if graph != nil {
-				for _, pkg := range r.workspacePackages {
-					if pkg.Path == r.packageDeps {
-						ownerName = pkg.Name
-						break
-					}
-				}
-			}
-			for depName := range pkgJSON.Dependencies {
-				if !workspaceNames[depName] {
-					allDeps[depName] = true
-					if graph != nil && ownerName != "" {
-						graph.AddDependency(ownerName, depName)
-					}
-				}
-			}
-		}
+		var ownerName string
+		if graph != nil {
+			for _, pkg := range r.workspacePackages {
+				if pkg.Path == r.packageDeps {
+					ownerName = pkg.Name
+					break
+				}
+			}
+		}
+		for depName := range pkgJSON.Dependencies {
+			if !workspaceNames[depName] {
+				allDeps[depName] = true
+				if graph != nil && ownerName != "" {
+					graph.AddDependency(ownerName, depName)
+				}
+			}
+		}
 	} else {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@resolve/local/local.go` around lines 578 - 596, The inner bare block that
scopes ownerName is unnecessary—remove the `{ ... }` and inline the ownerName
lookup so it shares the same scope as the rest of the logic; specifically locate
the code around r.workspacePackages / r.packageDeps where ownerName is set, move
the ownerName declaration out of the inner block (or extract a tiny helper like
findOwnerName(pkgPath) if preferred), then iterate over pkgJSON.Dependencies and
update allDeps, workspaceNames checks, and graph.AddDependency(ownerName,
depName) as before but without the extra brace-scoped block.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@resolve/local/local.go`:
- Around line 578-596: The inner bare block that scopes ownerName is
unnecessary—remove the `{ ... }` and inline the ownerName lookup so it shares
the same scope as the rest of the logic; specifically locate the code around
r.workspacePackages / r.packageDeps where ownerName is set, move the ownerName
declaration out of the inner block (or extract a tiny helper like
findOwnerName(pkgPath) if preferred), then iterate over pkgJSON.Dependencies and
update allDeps, workspaceNames checks, and graph.AddDependency(ownerName,
depName) as before but without the extra brace-scoped block.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0bbdb5f3-a4c1-49ab-b6c6-722e458f5b47

📥 Commits

Reviewing files that changed from the base of the PR and between dc4a910 and 4020d05.

⛔ Files ignored due to path filters (4)
  • testdata/workspace-package-deps/node_modules/lit/package.json is excluded by !**/node_modules/**
  • testdata/workspace-package-deps/node_modules/lodash/package.json is excluded by !**/node_modules/**
  • testdata/workspace-serve-root/node_modules/@lit/reactive-element/package.json is excluded by !**/node_modules/**
  • testdata/workspace-serve-root/node_modules/lit/package.json is excluded by !**/node_modules/**
📒 Files selected for processing (16)
  • cmd/generate/generate.go
  • npm/src/mappa.ts
  • resolve/local/local.go
  • resolve/local/local_test.go
  • resolve/local/options.go
  • testdata/workspace-package-deps/expected-all-deps.json
  • testdata/workspace-package-deps/expected-core-deps.json
  • testdata/workspace-package-deps/package.json
  • testdata/workspace-package-deps/packages/core/package.json
  • testdata/workspace-package-deps/packages/tools/package.json
  • testdata/workspace-serve-root/examples/kitchen-sink/package.json
  • testdata/workspace-serve-root/expected-no-rebase.json
  • testdata/workspace-serve-root/expected.json
  • testdata/workspace-serve-root/package.json
  • testdata/workspace-serve-root/packages/lib/package.json
  • wasm/main.go

Assisted-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@bennypowers

Copy link
Copy Markdown
Owner Author

@coderabbitai pause

@coderabbitai

coderabbitai Bot commented May 6, 2026

Copy link
Copy Markdown
✅ Actions performed

Reviews paused.

@bennypowers bennypowers merged commit 7fb03fe into main May 6, 2026
11 checks passed
@bennypowers bennypowers deleted the feat/relative-root branch May 6, 2026 15:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support serve root rebasing for workspace subdirectory resolution

1 participant