[PR #2081] [CLOSED] fix(javascript): retain TypeScript import-equals module dependencies #5010

Closed
opened 2026-10-31 00:57:57 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/2081
Author: @rekcilyssup
Created: 10/12/2026
Status: ❌ Closed

Base: main ← Head: fix/typescript-import-equals-dependencies


📝 Commits (10+)

  • 827ff56 fix(javascript): retain TypeScript import-equals module dependencies
  • 719d877 fix(javascript): reconcile import-equals analysis with main
  • e6f89a2 fix(javascript): share namespace exports and recover import aliases
  • 553efb8 fix(javascript): preserve complete namespace export ownership
  • e330633 fix(javascript): resolve dotted namespace owners
  • 205f021 test(javascript): expect module-root provenance for reopened export import
  • d22f2fc fix(javascript): defer shared namespace owners to enclosing scopes
  • dcf804e fix(javascript): share implicitly exported ambient namespace members
  • fd5019b fix(javascript): bind global augmentation members in program scope
  • 7dbe0fd fix(javascript): scope global augmentations outside the module scope

📊 Changes

11 files changed (+642 additions, -40 deletions)

View changed files

📝 src/application/javascript/JavaScriptModuleRelationships.ts (+3 -0)
📝 src/domain/javascript/javascriptSemanticAnalysis.require_links.test.ts (+128 -1)
📝 src/domain/javascript/javascriptSemanticAnalysis.ts (+153 -6)
📝 src/domain/javascript/javascriptSemanticAnalysisSteps.test.ts (+109 -0)
📝 src/domain/javascript/javascriptSemanticIr.ts (+2 -0)
📝 src/domain/javascript/javascriptSemanticProjection.ts (+2 -0)
📝 src/domain/javascript/javascriptSemanticState.ts (+63 -15)
📝 src/domain/javascript/javascriptSemanticValues.ts (+12 -2)
📝 src/javascript/analysis/JavaScriptAnalysisWorkerSchemas.ts (+22 -15)
📝 tests/boundary/filesystem/javascript-applications/javascriptApplicationTypeScript.test.ts (+99 -0)
📝 tests/boundary/javascript/javascriptAnalysisTransferBudgets.test.ts (+49 -1)

📄 Description

Summary

import value = require("./dep.cjs") in a .cts source parsed with complete coverage but contributed no import relationship and reported module_format: "unknown", while the TypeScript program it describes really loads the dependency. The TSImportEqualsDeclaration node was parsed but never classified by the binding, semantic module-link, or static reference producers, so the graph silently omitted a dependency the emitted program depends on. Fixes #2058.

Problem and expected behavior

  • Smallest trigger: a .cts file whose only dependency is an import-equals declaration, in a directory that also inventories the target.
  • Violated invariant: source module relationships must cover the dependencies of the emitted program, and the local alias must stay a real immutable binding that references resolve against.
  • Controls from the reported reproduction: tsc --module NodeNext exits 0, and the emitted main.cjs runs console.log(value) printing 42; the companion control.cjs using a plain require is already represented.
  • Expected: retain the value import-equals dependency with require semantics and the original declaration range, keep the immutable local alias, leave type-only declarations and internal namespace aliases free of runtime external dependencies, and keep an .mts source ESM when its import-equals compiles through createRequire.

Change and scope

  • bindInnerDeclaration (src/domain/javascript/javascriptSemanticAnalysis.ts) binds import-equals as an immutable import alias. Value external module references carry a module origin; type-only declarations and import alias = Some.Namespace aliases keep the local name with no origin, because TypeScript emits no load for either form.
  • collectSemanticModuleLink (src/domain/javascript/javascriptSemanticProjection.ts) projects a value external module reference as a kind: "require" link with importedName: "*", localName bound to the declaration and location equal to the declaration range, mirroring const x = require(...).
  • inspectNode (src/domain/javascript/javascriptStaticAnalysis.ts) emits the matching static require reference, so the legacy reference producer and the semantic producer agree.
  • moduleFormat (src/domain/javascript/JavaScriptModuleRelationships.ts) now treats .mts as ESM and .cts as CommonJS alongside .mjs/.cjs, because Node and TypeScript fix the module system by extension. Without this, the newly recovered require link would have flipped an .mts source to commonjs or mixed instead of staying ESM.
  • Intentionally not included: no change to import type { T } from ESM imports, namespace re-exports, package exports resolution, or the reference-source subsystem, which already parses import-equals separately.

Contract and boundary impact

  • Semantic owner and earliest changed stage: domain/javascript definition binding and module-link projection for TSImportEqualsDeclaration, then application/javascript module format classification.
  • CLI and MCP/tool-catalog contract: none — no schema, operation, or catalog change; new module_link_kind/kind edges and module_format values use existing vocabularies.
  • Provider, bridge, target-format, or platform compatibility: none; analysis stays static and reads no target.
  • Evidence, artifact, provenance, or reconstruction contract: the dependency edge keeps the declared specifier and the declaration's source range and file SHA; type-only and namespace-alias forms keep their local alias while reporting no dependency instead of an invented one.
  • Process execution, authorization, cleanup, or containment impact: none.
  • Generated metadata (docs/public/product-catalog.json), package, or installation impact: none.

Evidence and regression coverage

  • Tests added or updated: a domain test in javascriptSemanticAnalysis.require_links.test.ts covering the require link, the three immutable aliases, retained origin for the value form and empty origins for the erased forms, and reference resolution; an end-to-end test in JavaScriptApplicationTypeScript.test.ts covering edges, resolved_path, and module_format for .cts and .mts.
  • Base reproduction or other evidence: both new tests fail on base 7c207c79 when only the source changes are stashed (2 failed | 28 passed), and pass with the fix. TypeScript emission was executed as the loader control: .cts value form emits const value = require("./dep.cjs"), .cts type-only form emits no load at all, and .mts emits createRequire(import.meta.url) inside a .mjs output.
  • User-visible CLI/MCP output (if applicable): main.cts now reports two imports edges for ./dep.cjs (semantic module_link_kind: require, resolution_status: resolved, resolved_path: dep.cjs, plus the static kind: require edge) and module_format: "commonjs"; esm.mts reports the same dependency with module_format: "esm"; type.cts and alias.cts report no import edges; coverage.status stays complete.
  • Remaining proof gaps: tests do not invoke tsc; the emission facts above were executed once while developing the fix and are stated as derived evidence.

For evidence-bearing changes:

  • Observed, derived, and inferred claims remain distinguishable.
  • Artifact identity, source provenance, and failed attempts remain preserved.
  • Unsupported, incomplete, unavailable, or uncertain outcomes remain visible.

Validation performed

  • npx vitest run src/domain/javascript/ src/application/javascript/ — 62 files, 1129 tests passed.
  • npx vitest run src/domain/javascript/javascriptSemanticAnalysis.require_links.test.ts src/application/javascript/JavaScriptApplicationTypeScript.test.ts — 30 tests passed; with the source changes stashed, the 2 new tests fail.
  • npx vitest run tests/boundary/filesystem/javascriptPackageExportTargets.test.ts tests/boundary/javascript/packageConditionalExports.test.ts tests/composition/javascript-applications/electronApplicationAnalysis.test.ts tests/acceptance/applications/electronPreloadOverridesCli.test.ts tests/acceptance/applications/packageSubpathsCli.test.ts — 5 files, 128 tests passed.
  • npx vitest run src/domain/referenceSourceGraph.test.ts — 13 tests passed.
  • npm run check:fast — typecheck and lint passed.
  • Issue reproduction probe (scripts/rea.mjs analyze-javascript-application) — main.cts two resolved require edges with module_format: "commonjs", esm.mts dependency with module_format: "esm", type.cts and alias.cts zero import edges, coverage.status complete.

Compatibility, safety, and release

  • Breaking changes or migration steps: none for valid input. Callers gain edges that were previously missing and see module_format move from unknown (or a content-derived guess) to the extension-fixed value for .mts and .cts sources.
  • Real Hopper/Ghidra, browser, or OS coverage: not applicable — static TypeScript source analysis only. Node.js and tsc were executed as controls.
  • Package or release metadata impact: none.
  • Security, privacy, process, or containment review: none; the change only adds classification of an already-parsed declaration and keeps resolution confined to the inventoried container.

Review checklist

  • This PR addresses a concrete problem or an agreed enhancement.
  • The PR has one focused outcome and the title follows type(scope): outcome.
  • Related issue is linked, or the reason for not linking one is stated above.
  • Tests cover changed observable behavior and meaningful failure paths.
  • Owning docs, contracts, and generated metadata are updated where needed.
  • User-visible CLI/MCP changes include representative output.
  • I checked the final diff for secrets, unrelated cleanup, and unsupported claims.

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/morluto/rea/pull/2081 **Author:** [@rekcilyssup](https://github.com/rekcilyssup) **Created:** 10/12/2026 **Status:** ❌ Closed **Base:** `main` ← **Head:** `fix/typescript-import-equals-dependencies` --- ### 📝 Commits (10+) - [`827ff56`](https://github.com/morluto/rea/commit/827ff567ad81befb743941c106e8669006a15ebe) fix(javascript): retain TypeScript import-equals module dependencies - [`719d877`](https://github.com/morluto/rea/commit/719d87778adc7dffd126ec1a26f624a6f8ca1909) fix(javascript): reconcile import-equals analysis with main - [`e6f89a2`](https://github.com/morluto/rea/commit/e6f89a2247e56d51bce40a6f702a79e89862e112) fix(javascript): share namespace exports and recover import aliases - [`553efb8`](https://github.com/morluto/rea/commit/553efb800c8a085af102382be4dba69cd39bc900) fix(javascript): preserve complete namespace export ownership - [`e330633`](https://github.com/morluto/rea/commit/e3306333a5fe799dcc8b8c1feadddfc9e8277b76) fix(javascript): resolve dotted namespace owners - [`205f021`](https://github.com/morluto/rea/commit/205f02129236ab0765cf2c17d0668ec7ddf7ff7d) test(javascript): expect module-root provenance for reopened export import - [`d22f2fc`](https://github.com/morluto/rea/commit/d22f2fc8265a3fcd8ba2801ef3314ccf9b0dc073) fix(javascript): defer shared namespace owners to enclosing scopes - [`dcf804e`](https://github.com/morluto/rea/commit/dcf804e57e4d36e6fba19b2048dc62ead0468ef2) fix(javascript): share implicitly exported ambient namespace members - [`fd5019b`](https://github.com/morluto/rea/commit/fd5019b5063005863b412b761bace6991a546af3) fix(javascript): bind global augmentation members in program scope - [`7dbe0fd`](https://github.com/morluto/rea/commit/7dbe0fd6d0b4d7233e0abf3327c59ac3ff451801) fix(javascript): scope global augmentations outside the module scope ### 📊 Changes **11 files changed** (+642 additions, -40 deletions) <details> <summary>View changed files</summary> 📝 `src/application/javascript/JavaScriptModuleRelationships.ts` (+3 -0) 📝 `src/domain/javascript/javascriptSemanticAnalysis.require_links.test.ts` (+128 -1) 📝 `src/domain/javascript/javascriptSemanticAnalysis.ts` (+153 -6) 📝 `src/domain/javascript/javascriptSemanticAnalysisSteps.test.ts` (+109 -0) 📝 `src/domain/javascript/javascriptSemanticIr.ts` (+2 -0) 📝 `src/domain/javascript/javascriptSemanticProjection.ts` (+2 -0) 📝 `src/domain/javascript/javascriptSemanticState.ts` (+63 -15) 📝 `src/domain/javascript/javascriptSemanticValues.ts` (+12 -2) 📝 `src/javascript/analysis/JavaScriptAnalysisWorkerSchemas.ts` (+22 -15) 📝 `tests/boundary/filesystem/javascript-applications/javascriptApplicationTypeScript.test.ts` (+99 -0) 📝 `tests/boundary/javascript/javascriptAnalysisTransferBudgets.test.ts` (+49 -1) </details> ### 📄 Description ## Summary `import value = require("./dep.cjs")` in a `.cts` source parsed with complete coverage but contributed no import relationship and reported `module_format: "unknown"`, while the TypeScript program it describes really loads the dependency. The `TSImportEqualsDeclaration` node was parsed but never classified by the binding, semantic module-link, or static reference producers, so the graph silently omitted a dependency the emitted program depends on. Fixes #2058. ## Problem and expected behavior - Smallest trigger: a `.cts` file whose only dependency is an import-equals declaration, in a directory that also inventories the target. - Violated invariant: source module relationships must cover the dependencies of the emitted program, and the local alias must stay a real immutable binding that references resolve against. - Controls from the reported reproduction: `tsc --module NodeNext` exits 0, and the emitted `main.cjs` runs `console.log(value)` printing `42`; the companion `control.cjs` using a plain `require` is already represented. - Expected: retain the value import-equals dependency with require semantics and the original declaration range, keep the immutable local alias, leave type-only declarations and internal namespace aliases free of runtime external dependencies, and keep an `.mts` source ESM when its import-equals compiles through `createRequire`. ## Change and scope - `bindInnerDeclaration` (`src/domain/javascript/javascriptSemanticAnalysis.ts`) binds import-equals as an immutable `import` alias. Value external module references carry a module origin; type-only declarations and `import alias = Some.Namespace` aliases keep the local name with no origin, because TypeScript emits no load for either form. - `collectSemanticModuleLink` (`src/domain/javascript/javascriptSemanticProjection.ts`) projects a value external module reference as a `kind: "require"` link with `importedName: "*"`, `localName` bound to the declaration and `location` equal to the declaration range, mirroring `const x = require(...)`. - `inspectNode` (`src/domain/javascript/javascriptStaticAnalysis.ts`) emits the matching static `require` reference, so the legacy reference producer and the semantic producer agree. - `moduleFormat` (`src/domain/javascript/JavaScriptModuleRelationships.ts`) now treats `.mts` as ESM and `.cts` as CommonJS alongside `.mjs`/`.cjs`, because Node and TypeScript fix the module system by extension. Without this, the newly recovered `require` link would have flipped an `.mts` source to `commonjs` or `mixed` instead of staying ESM. - Intentionally not included: no change to `import type { T } from` ESM imports, namespace re-exports, package `exports` resolution, or the reference-source subsystem, which already parses import-equals separately. ## Contract and boundary impact - Semantic owner and earliest changed stage: `domain/javascript` definition binding and module-link projection for `TSImportEqualsDeclaration`, then `application/javascript` module format classification. - CLI and MCP/tool-catalog contract: none — no schema, operation, or catalog change; new `module_link_kind`/`kind` edges and `module_format` values use existing vocabularies. - Provider, bridge, target-format, or platform compatibility: none; analysis stays static and reads no target. - Evidence, artifact, provenance, or reconstruction contract: the dependency edge keeps the declared specifier and the declaration's source range and file SHA; type-only and namespace-alias forms keep their local alias while reporting no dependency instead of an invented one. - Process execution, authorization, cleanup, or containment impact: none. - Generated metadata (`docs/public/product-catalog.json`), package, or installation impact: none. ## Evidence and regression coverage - Tests added or updated: a domain test in `javascriptSemanticAnalysis.require_links.test.ts` covering the require link, the three immutable aliases, retained origin for the value form and empty origins for the erased forms, and reference resolution; an end-to-end test in `JavaScriptApplicationTypeScript.test.ts` covering edges, `resolved_path`, and `module_format` for `.cts` and `.mts`. - Base reproduction or other evidence: both new tests fail on base `7c207c79` when only the source changes are stashed (`2 failed | 28 passed`), and pass with the fix. TypeScript emission was executed as the loader control: `.cts` value form emits `const value = require("./dep.cjs")`, `.cts` type-only form emits no load at all, and `.mts` emits `createRequire(import.meta.url)` inside a `.mjs` output. - User-visible CLI/MCP output (if applicable): `main.cts` now reports two `imports` edges for `./dep.cjs` (semantic `module_link_kind: require`, `resolution_status: resolved`, `resolved_path: dep.cjs`, plus the static `kind: require` edge) and `module_format: "commonjs"`; `esm.mts` reports the same dependency with `module_format: "esm"`; `type.cts` and `alias.cts` report no import edges; `coverage.status` stays `complete`. - Remaining proof gaps: tests do not invoke `tsc`; the emission facts above were executed once while developing the fix and are stated as derived evidence. For evidence-bearing changes: - [x] Observed, derived, and inferred claims remain distinguishable. - [x] Artifact identity, source provenance, and failed attempts remain preserved. - [x] Unsupported, incomplete, unavailable, or uncertain outcomes remain visible. ## Validation performed - `npx vitest run src/domain/javascript/ src/application/javascript/` — 62 files, 1129 tests passed. - `npx vitest run src/domain/javascript/javascriptSemanticAnalysis.require_links.test.ts src/application/javascript/JavaScriptApplicationTypeScript.test.ts` — 30 tests passed; with the source changes stashed, the 2 new tests fail. - `npx vitest run tests/boundary/filesystem/javascriptPackageExportTargets.test.ts tests/boundary/javascript/packageConditionalExports.test.ts tests/composition/javascript-applications/electronApplicationAnalysis.test.ts tests/acceptance/applications/electronPreloadOverridesCli.test.ts tests/acceptance/applications/packageSubpathsCli.test.ts` — 5 files, 128 tests passed. - `npx vitest run src/domain/referenceSourceGraph.test.ts` — 13 tests passed. - `npm run check:fast` — typecheck and lint passed. - Issue reproduction probe (`scripts/rea.mjs analyze-javascript-application`) — `main.cts` two resolved `require` edges with `module_format: "commonjs"`, `esm.mts` dependency with `module_format: "esm"`, `type.cts` and `alias.cts` zero import edges, `coverage.status` `complete`. ## Compatibility, safety, and release - Breaking changes or migration steps: none for valid input. Callers gain edges that were previously missing and see `module_format` move from `unknown` (or a content-derived guess) to the extension-fixed value for `.mts` and `.cts` sources. - Real Hopper/Ghidra, browser, or OS coverage: not applicable — static TypeScript source analysis only. Node.js and `tsc` were executed as controls. - Package or release metadata impact: none. - Security, privacy, process, or containment review: none; the change only adds classification of an already-parsed declaration and keeps resolution confined to the inventoried container. ## Review checklist - [x] This PR addresses a concrete problem or an agreed enhancement. - [x] The PR has one focused outcome and the title follows `type(scope): outcome`. - [x] Related issue is linked, or the reason for not linking one is stated above. - [x] Tests cover changed observable behavior and meaningful failure paths. - [x] Owning docs, contracts, and generated metadata are updated where needed. - [x] User-visible CLI/MCP changes include representative output. - [x] I checked the final diff for secrets, unrelated cleanup, and unsupported claims. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-10-31 00:57:57 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#5010
No description provided.