Fix: resolve unique --target names in monorepos (#700)

Bare child names such as Cantaro.Web now match a unique workspace candidate instead of being reported missing.

AI assistance: Cursor Grok 4.6 implemented this change.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Abdul Wahab
2026-09-02 11:00:51 +05:00
co-authored by Cursor
parent c0f4952122
commit b226c71de7
2 changed files with 77 additions and 3 deletions
+17 -3
View File
@@ -248,10 +248,25 @@ function resolveEnvContextDir(cwd) {
return path.isAbsolute(trimmed) ? trimmed : path.resolve(cwd, trimmed);
}
function resolveTargetPath(cwd, targetPath) {
const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(cwd, targetPath);
if (fs.existsSync(abs)) return abs;
return findUniqueBareTarget(cwd, targetPath) || abs;
}
function findUniqueBareTarget(cwd, targetPath) {
if (path.isAbsolute(targetPath) || /[\\/]/.test(targetPath)) return null;
const repoRoot = findMonorepoRoot(path.resolve(cwd));
if (!repoRoot) return null;
const matches = discoverTargetCandidates(repoRoot).filter((candidate) => candidate.name === targetPath);
if (matches.length !== 1) return null;
return path.resolve(repoRoot, matches[0].path);
}
function resolveTargetDir(cwd, options = {}) {
const targetPath = options && typeof options === 'object' ? options.targetPath : null;
if (!targetPath || !String(targetPath).trim()) return cwd;
const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(cwd, targetPath);
const abs = resolveTargetPath(cwd, targetPath);
try {
const stat = fs.statSync(abs);
return stat.isDirectory() ? abs : path.dirname(abs);
@@ -1272,8 +1287,7 @@ function hasTargetOption(options) {
}
function pathExistsForTarget(cwd, targetPath) {
const abs = path.isAbsolute(targetPath) ? targetPath : path.resolve(cwd, targetPath);
return fs.existsSync(abs);
return fs.existsSync(resolveTargetPath(cwd, targetPath));
}
const HOOK_MANIFESTS_BY_PROVIDER = Object.freeze({
+60
View File
@@ -770,6 +770,66 @@ describe('loadContext (monorepo project context)', () => {
assert.match(res.stdout, /MONOREPO_TARGET_REQUIRED/);
});
it('resolves a unique workspace child by bare basename via --target', () => {
writeMonorepo();
write('apps/dashboard/PRODUCT.md', '# Dashboard product\n\n## Platform\n\nweb\n');
const res = spawnSync(process.execPath, [SCRIPT_PATH, '--target', 'dashboard'], {
cwd: scratch,
encoding: 'utf8',
env: { ...process.env, IMPECCABLE_NO_UPDATE_CHECK: '1', IMPECCABLE_NO_STALENESS_CHECK: '1' },
});
assert.equal(res.status, 0, res.stderr);
assert.match(res.stdout, /# Dashboard product/);
assert.match(res.stdout, /"targetExists": true/);
assert.match(res.stdout, /"targetPath": "dashboard"/);
assert.doesNotMatch(res.stdout, /MONOREPO_TARGET_REQUIRED/);
const ctx = loadContext(scratch, { targetPath: 'dashboard' });
assert.equal(ctx.projectRoot, path.join(scratch, 'apps', 'dashboard'));
});
it('resolves a dotted workspace child name by bare basename via --target', () => {
write('package.json', JSON.stringify({
private: true,
workspaces: ['src/*'],
}, null, 2));
write('src/Cantaro.Web/PRODUCT.md', '# Cantaro Web product\n\n## Platform\n\nweb\n');
write('src/Cantaro.Api/package.json', JSON.stringify({ name: 'cantaro-api' }));
const res = spawnSync(process.execPath, [SCRIPT_PATH, '--target', 'Cantaro.Web'], {
cwd: scratch,
encoding: 'utf8',
env: { ...process.env, IMPECCABLE_NO_UPDATE_CHECK: '1', IMPECCABLE_NO_STALENESS_CHECK: '1' },
});
assert.equal(res.status, 0, res.stderr);
assert.match(res.stdout, /# Cantaro Web product/);
assert.match(res.stdout, /"targetExists": true/);
assert.match(res.stdout, /"targetPath": "Cantaro\.Web"/);
const ctx = loadContext(scratch, { targetPath: 'Cantaro.Web' });
assert.equal(ctx.projectRoot, path.join(scratch, 'src', 'Cantaro.Web'));
});
it('does not guess when a bare basename matches multiple workspace children', () => {
write('package.json', JSON.stringify({
private: true,
workspaces: ['apps/*', 'packages/*'],
}, null, 2));
write('turbo.json', JSON.stringify({ tasks: {} }));
write('apps/web/src/App.jsx', 'export default null;\n');
write('packages/web/src/index.ts', 'export {};\n');
const res = spawnSync(process.execPath, [SCRIPT_PATH, '--target', 'web'], {
cwd: scratch,
encoding: 'utf8',
env: { ...process.env, IMPECCABLE_NO_UPDATE_CHECK: '1', IMPECCABLE_NO_STALENESS_CHECK: '1' },
});
assert.equal(res.status, 0, res.stderr);
assert.match(res.stdout, /"targetExists": false/);
assert.match(res.stdout, /MONOREPO_TARGET_REQUIRED/);
const ctx = loadContext(scratch, { targetPath: 'web' });
assert.equal(ctx.projectRoot, scratch);
});
it('asks for app selection even when root PRODUCT.md is absent', () => {
write('package.json', JSON.stringify({
private: true,