Current Behavior
When a TypeScript file contains an import X = Y.Z declaration (namespace import-equals), the Nx native Rust scanner gets into a broken state and starts misinterpreting subsequent string literals as module specifiers. On case-insensitive filesystems (macOS HFS+/APFS default), these string literals then resolve against node_modules, producing
phantom npm dependencies in the project graph.
For example, a file with import KanalEnum = SomeEnum.Enum followed by case 'Open': causes Nx to record npm:open as a static dependency — because node_modules/Open resolves to node_modules/open on a case-insensitive filesystem.
This breaks @nx/dependency-checks: with open listed in package.json, macOS lint passes (the scanner thinks it's used), but Linux CI reports it as "not used by the project".
Removing it from package.json flips the problem — macOS reports it as "missing from dependencies", while Linux CI passes. There is no configuration that satisfies both platforms
without using ignoredDependencies.
This is the same class of bug as #8938, where the old TS-based scanner misidentified template literal strings as import statements (fixed in #10360). The current native Rust scanner has a similar problem, specifically triggered by the import = syntax.
Expected Behavior
The import X = Y.Z syntax is a TypeScript namespace alias — not a module import. The scanner should not treat it as a module specifier, and it should not affect parsing of subsequent code in the file. String literals should never be resolved as npm dependencies.
Expected Behavior
String literals ('Open', "Open") should not be treated as module specifiers. Only actual import/require statements should be analyzed for npm dependency detection.
GitHub Repo
No response
Steps to Reproduce
- On macOS (default case-insensitive APFS), create a workspace with a library 2. Ensure a lowercase npm package (e.g.
debug) is installed — even as a transitive dependency 3. Add a .ts file with an import = declaration and a PascalCase string literal matching the package name:
import { Component } from '@angular/core'
export namespace Foo {
export enum Bar { A = 'A' }
}
import MyBar = Foo.Bar // <-- this breaks the scanner's state
export type LogLevel = 'Debug' | 'Info' | 'Warn' | 'Error'
export function getLevel(): LogLevel {
return 'Debug' // <-- this gets falsely resolved to npm:debug
}
- Run
npx nx reset && npx nx graph --file=output.json
- Inspect the output — the project graph includes
npm:debug as a "type": "static" dependency
Control test — remove import =, keep everything else identical:
import { Component } from '@angular/core'
export namespace Foo {
export enum Bar { A = 'A' }
}
// removed: import MyBar = Foo.Bar
export type LogLevel = 'Debug' | 'Info' | 'Warn' | 'Error'
export function getLevel(): LogLevel {
return 'Debug' // <-- no longer falsely detected
}
Result: npm:debug disappears from the project graph. The import = syntax is the trigger.
Proof that the filesystem is the second factor:
# macOS (case-insensitive) — resolves:
$ ls node_modules/Debug/package.json
node_modules/Debug/package.json
# Linux (case-sensitive) — would not resolve:
# ls: cannot access 'node_modules/Debug/package.json': No such file or directory
Full proof from a real project:
We traced this to helpers.ts which contains import SomelEnum = RechnungUploadCommit.KanalEnum and string literals 'Open' in switch/case statements. The Nx file map reports:
FILE: invoice.helpers.ts
DEPS: ["npm:@angular/common","@my/api","@my/shared","npm:open"]
The TypeScript compiler confirms NO actual import of open:
Actual imports: ['@angular/common', '@angular/common/http', '@my/api/leistung',
'@my/shared', '../../statements/...', '../model/invoice.model']
Contains 'open'? false
Renaming 'Open' to 'Xopen' and rebuilding the graph: npm:open disappears. Restoring 'Open': it reappears.
Nx Report
Node : 22.22.0
OS : darwin-arm64
Native Target : aarch64-macos
npm : 10.9.4
daemon : Available
nx : 22.4.1
lerna : 9.0.3
@nx/js : 22.4.1
@nx/eslint : 22.4.1
@nx/workspace : 22.4.1
@nx/angular : 22.4.1
@nx/jest : 22.4.1
@nx/cypress : 22.4.1
@nx/devkit : 22.4.1
@nx/eslint-plugin : 22.4.1
@nx/module-federation : 22.4.1
@nx/rspack : 22.4.1
@nx/storybook : 22.4.1
@nx/web : 22.4.1
@nx/webpack : 22.4.1
typescript : 5.9.3
---------------------------------------
Registered Plugins:
@nx/storybook/plugin
---------------------------------------
Community plugins:
@storybook/angular : 10.2.8
angular-eslint : 21.1.0
Failure Logs
Package Manager Version
npm 10.9.4
Operating System
Additional Information
- Two conditions required: (1) a file contains
import X = Y.Z syntax, AND (2) the same file contains a string literal that case-insensitively matches an installed npm package name
- Only affects case-insensitive filesystems (macOS default). Does not reproduce on Linux CI (case-sensitive ext4/etc.)
- Impact on
@nx/dependency-checks: The rule cannot be satisfied on both platforms. Including the package makes CI fail ("not used"), removing it makes macOS fail ("missing"). The only workaround is ignoredDependencies.
- Workaround: Add the falsely detected package to
ignoredDependencies in the @nx/dependency-checks rule config.
Current Behavior
When a TypeScript file contains an
import X = Y.Zdeclaration (namespace import-equals), the Nx native Rust scanner gets into a broken state and starts misinterpreting subsequent string literals as module specifiers. On case-insensitive filesystems (macOS HFS+/APFS default), these string literals then resolve againstnode_modules, producingphantom npm dependencies in the project graph.
For example, a file with
import KanalEnum = SomeEnum.Enumfollowed bycase 'Open':causes Nx to recordnpm:openas a static dependency — becausenode_modules/Openresolves tonode_modules/openon a case-insensitive filesystem.This breaks @nx/dependency-checks: with open listed in package.json, macOS lint passes (the scanner thinks it's used), but Linux CI reports it as "not used by the project".
Removing it from package.json flips the problem — macOS reports it as "missing from dependencies", while Linux CI passes. There is no configuration that satisfies both platforms
without using ignoredDependencies.
This is the same class of bug as #8938, where the old TS-based scanner misidentified template literal strings as import statements (fixed in #10360). The current native Rust scanner has a similar problem, specifically triggered by the
import =syntax.Expected Behavior
The
import X = Y.Zsyntax is a TypeScript namespace alias — not a module import. The scanner should not treat it as a module specifier, and it should not affect parsing of subsequent code in the file. String literals should never be resolved as npm dependencies.Expected Behavior
String literals (
'Open',"Open") should not be treated as module specifiers. Only actualimport/requirestatements should be analyzed for npm dependency detection.GitHub Repo
No response
Steps to Reproduce
debug) is installed — even as a transitive dependency 3. Add a.tsfile with animport =declaration and a PascalCase string literal matching the package name:npx nx reset && npx nx graph --file=output.jsonnpm:debugas a"type": "static"dependencyControl test — remove
import =, keep everything else identical:Result:
npm:debugdisappears from the project graph. Theimport =syntax is the trigger.Proof that the filesystem is the second factor:
Full proof from a real project:
We traced this to
helpers.tswhich containsimport SomelEnum = RechnungUploadCommit.KanalEnumand string literals'Open'in switch/case statements. The Nx file map reports:The TypeScript compiler confirms NO actual import of
open:Renaming
'Open'to'Xopen'and rebuilding the graph:npm:opendisappears. Restoring'Open': it reappears.Nx Report
Failure Logs
Package Manager Version
npm 10.9.4
Operating System
Additional Information
import X = Y.Zsyntax, AND (2) the same file contains a string literal that case-insensitively matches an installed npm package name@nx/dependency-checks: The rule cannot be satisfied on both platforms. Including the package makes CI fail ("not used"), removing it makes macOS fail ("missing"). The only workaround is ignoredDependencies.ignoredDependenciesin the@nx/dependency-checksrule config.