Skip to content

Commit 82e7afe

Browse files
AgentEnderclaude
andauthored
feat(js): support nx.sync.ignoredDependencies in typescript-sync (#35401)
## Current Behavior `@nx/js:typescript-sync` always materializes every project-graph edge of a project as a TypeScript project reference in the corresponding runtime tsconfig. The existing `nx.sync.ignoredReferences` opt-out keeps user-authored reference paths from being pruned, but there is no way to tell the generator "don't add a reference for this dependency in the first place." That becomes a problem when the project graph contains intentional cycles — for example when `@nx/workspace` declares its lazy-loaded plugin peers (`@nx/js`, `@nx/angular`, etc.) as optional peers, and those same plugins depend back on `@nx/workspace`. Materializing both edges as TS project references produces `TS6202: Project references may not form a circular graph`. The only workaround today is `implicitDependencies: ["!name", …]` in `project.json`, which removes the edge from the project graph entirely and hides it from every other consumer (dependency tooling, graph visualizations, supply-chain audits). ## Expected Behavior Tsconfig files now accept an `nx.sync.ignoredDependencies: string[]` field (sibling of the existing `nx.sync.ignoredReferences`). When the sync generator processes that tsconfig, any project-graph dependency whose project name is in the set is skipped — no new reference is added for it, and any existing reference for that dependency is pruned as stale. This lets a workspace keep real, cyclic project-graph edges (so the package.json peer relationships stay visible) while opting the affected tsconfig out of materializing the cycle into project references. The task graph is kept acyclic separately via explicit `dependsOn` entries. Co-authored-by: Claude <[email protected]>
1 parent 7af6da1 commit 82e7afe

2 files changed

Lines changed: 44 additions & 0 deletions

File tree

packages/js/src/generators/typescript-sync/typescript-sync.spec.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,40 @@ describe('syncGenerator()', () => {
482482
`);
483483
});
484484

485+
it('should skip dependencies listed in `nx.sync.ignoredDependencies` and prune existing references for them', async () => {
486+
// b depends on both a and c, but its tsconfig opts out of c via
487+
// ignoredDependencies. The reference to c should not be added (and an
488+
// existing stale one should be pruned).
489+
addProject('c');
490+
projectGraph.dependencies['b'].push({
491+
type: 'static',
492+
source: 'b',
493+
target: 'c',
494+
});
495+
writeJson(tree, 'packages/b/tsconfig.json', {
496+
compilerOptions: {
497+
composite: true,
498+
},
499+
references: [{ path: '../c' }],
500+
nx: {
501+
sync: {
502+
ignoredDependencies: ['c'],
503+
},
504+
},
505+
});
506+
507+
await syncGenerator(tree);
508+
509+
expect(readJson(tree, 'packages/b/tsconfig.json').references)
510+
.toMatchInlineSnapshot(`
511+
[
512+
{
513+
"path": "../a",
514+
},
515+
]
516+
`);
517+
});
518+
485519
it('should not prune stale project references from projects included in `nx.sync.ignoredReferences`', async () => {
486520
writeJson(tree, 'packages/b/tsconfig.json', {
487521
compilerOptions: {

packages/js/src/generators/typescript-sync/typescript-sync.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ interface Tsconfig {
2828
nx?: {
2929
sync?: {
3030
ignoredReferences?: string[];
31+
ignoredDependencies?: string[];
3132
};
3233
};
3334
}
@@ -392,6 +393,9 @@ function updateTsConfigReferences(
392393
);
393394
const tsConfig = parseJson<Tsconfig>(stringifiedJsonContents);
394395
const ignoredReferences = new Set(tsConfig.nx?.sync?.ignoredReferences ?? []);
396+
const ignoredDependencies = new Set(
397+
tsConfig.nx?.sync?.ignoredDependencies ?? []
398+
);
395399

396400
// We have at least one dependency so we can safely set it to an empty array if not already set
397401
const references = [];
@@ -448,6 +452,12 @@ function updateTsConfigReferences(
448452
}
449453

450454
for (const dep of dependencies) {
455+
if (ignoredDependencies.has(dep.name)) {
456+
// The user has explicitly opted out of this dependency edge, typically
457+
// to break a circular project reference graph that the project graph
458+
// intentionally allows.
459+
continue;
460+
}
451461
// Ensure the project reference for the target is set if we can find the
452462
// relevant tsconfig file
453463
let referencePath: string;

0 commit comments

Comments
 (0)