Skip to content

cowork: namespace & side-effect imports consume a module's whole export surface - #49

Draft
Coding-Dev-Tools wants to merge 10 commits into
masterfrom
cowork/improve-deadcode
Draft

cowork: namespace & side-effect imports consume a module's whole export surface#49
Coding-Dev-Tools wants to merge 10 commits into
masterfrom
cowork/improve-deadcode

Conversation

@Coding-Dev-Tools

Copy link
Copy Markdown
Owner

Problem

The dead-code scanner ignored two common import forms entirely:

  • import * as Utils from './utils' (namespace import)
  • import './polyfill' (bare side-effect import)

Exports consumed only through these forms were falsely reported as unused_export with removable=True — meaning deadcode remove would blank live code. Reproduced pre-fix: a module whose only consumer used Utils.helper() was flagged removable.

Fix

Both forms now resolve their module specifier like barrel star-reexports already do (_resolve_relative_module): when the target resolves to a scanned file, its entire export surface is treated as used. Bare package specifiers (e.g. 'lodash') stay unresolvable and keep flagging local modules correctly.

Tests

New tests/test_namespace_sideeffect_imports.py (5 cases):

  1. namespace import marks all target exports used
  2. export * as ns from ... re-export ditto
  3. bare-specifier namespace import cannot mark local module used
  4. side-effect import marks all target exports used
  5. control: no consumer still flags exports

Full suite: 121 passed, ruff clean.

DevForge Engineer and others added 10 commits July 8, 2026 01:34
…s Revenue Holdings / stale 2026 year); W-directed fleet-wide pass
…d-code scan

Named (`export { X } from './mod'`), renamed (`export { X as Y }`),
type (`export { type X }`), and star (`export * from './mod'`) re-exports
now mark the forwarded symbols as used, so barrel/index files no longer
produce false-positive 'unused_export' findings flagged removable=True
(which could delete live public API). Resolves `export *` specifiers to
scanned files (incl. directory index.*). Adds TestReexportForwarding
(8 cases) + removes a pre-existing F841 unused var. 113 tests pass, ruff clean.
… mixed default+named imports, and correct group-index reversal

- Rewrote _IMPORT_PATTERN regex to handle: import type {Foo}, import Default, {Named},
  import {type Foo}, and import Foo as Bar forms
- Fixed _parse_imports group-number reversal (group 1 = named imports block, group 2 = default)
- Strips 'type ' prefix from named import entries in both named-block positions
- All 113 existing tests pass; ruff clean
…code

# Conflicts:
#	CHANGELOG.md
#	src/deadcode/scanner.py
#	tests/test_scanner.py
…fect imports as whole-module consumption

A namespace binding (import * as Utils from './utils') or a bare side-effect
import (import './polyfill') consumes the target module's entire export
surface. The scanner previously ignored both forms entirely, so exports used
ONLY through them were falsely reported as unused with removable=True — live
code queued for deletion by 'deadcode remove'. Both now resolve like barrel
star-reexports: the resolved module's exports are treated as used. Bare
package specifiers stay unresolvable and keep flagging.

+5 regression tests (namespace, export * as ns, side-effect, bare-specifier,
no-consumer control). Full suite: 121 passed, ruff clean.
The previous commit (2ef1848) was built from a stale temp index and
accidentally recorded deletions of 34 unrelated tracked files. This commit
restores the full tree of 30e09bb while keeping the intended scanner fix
(namespace/side-effect imports as whole-module consumption) and its 5
regression tests. No force-push used.
…heckout-index

efa7ce2 restored the tree but its checkout-index step reverted
src/deadcode/scanner.py to the pre-fix version. This commit re-applies the
scanner fix from 2ef1848: import * as NS / bare side-effect imports consume
the target module's whole export surface (resolves like barrel star-reexports).

Final tree vs master-base 30e09bb = exactly scanner.py fix + 5-test file.
@github-actions

Copy link
Copy Markdown

🤖 Automated Code Review

✅ Ruff Lint — No issues

⚠️ Ruff Format — Formatting needed

unformatted: File would be reformatted
 --> src/deadcode/__main__.py:2:1
  |
1 | """Allow running deadcode as: python -m deadcode"""
2 +
3 | from .cli import cli
  |

unformatted: File would be reformatted
   --> src/deadcode/cli.py:66:15
    |
65  | @click.option("--project", "-p", default=".", help="Project directory to scan")
66  + @click.option("--ignore", "-i", multiple=True, help="Additional ignore patterns (gitignore-style)")
67  | @click.option(
    -     "--ignore", "-i", multiple=True, help="Additional ignore patterns (gitignore-style)"
    - )
    - @click.option(
68  |     "--include",
--------------------------------------------------------------------------------
73  | @click.pass_context
    - def cli(
    -     ctx: click.Context, project: str, ignore: tuple[str, ...], include: tuple[str, ...]
    - ) -> None:
74  + def cli(ctx: click.Context, project: str, ignore: tuple[str, ...], include: tuple[str, ...]) -> None:
75  |     """DeadCode — Find and remove dead code in TS/React/Next.js projects.
--------------------------------------------------------------------------------
112 | @cli.command()
    - @click.option(
    -     "--json-output", "-j", is_flag=True, help="Alias for --format=json (deprecated)"
    - )

✅ Secret Detection — Clean

✅ Large Files — Within limits

📊 Diff Stats — 2 file(s) changed

 src/deadcode/scanner.py                    | 32 ++++++++++--
 tests/test_namespace_sideeffect_imports.py | 80 ++++++++++++++++++++++++++++++
 2 files changed, 108 insertions(+), 4 deletions(-)

Verdict: ⚠️ Warnings — Lint/format issues found. Recommend fixing before merge.

Automated by Coding-Dev-Tools/.github reusable workflow.

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.

1 participant