[GH-ISSUE #6859] [Bug] Historical reference import omits TypeScript import-type dependencies #4199

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

Originally created by @coygeek on GitHub (Oct 23, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/6859

Before submitting

  • I searched the open and closed issues for a related report.
  • I removed credentials, tokens, private paths, and sensitive target data.

Area

Evidence, artifacts, or reconstruction

Observed behavior

import-reference-source silently omits the dependency in both export type Widget = import("./dep").Widget and export type DepModule = typeof import("./dep"). Both files are inventoried and hashed, with no parse failures or entry-specific limitations. The equivalent import type { Widget } from "./dep" declaration produces the expected parsed internal edge. All three public source-CLI calls exit 0.

Reproduction

From the source checkout identified below, with its locked dependencies already present, save this script as /tmp/reference-import-types.mjs. The exact script body ran once with scratch paths through the registered createCli().serve() command; the command below normalizes only those temporary paths. It creates inert UTF-8 fixtures and executes REA, never the fixture modules. No build is needed.

node --import ./node_modules/tsx/dist/loader.mjs \
  /tmp/reference-import-types.mjs "$PWD" /tmp/reference-import-types-fixtures
import { mkdir, writeFile } from 'node:fs/promises';
import { join } from 'node:path';
import { pathToFileURL } from 'node:url';
const [repo, out] = process.argv.slice(2);
const { createCli, closeCliJavaScriptResources } = await import(pathToFileURL(join(repo, 'src/cli.ts')).href);
const cases = {
  import_type: 'export type Widget = import("./dep").Widget;\n',
  typeof_import: 'export type DepModule = typeof import("./dep");\n',
  declaration_control: 'import type { Widget } from "./dep"; export type Output = Widget;\n',
};
try {
  for (const [name, source] of Object.entries(cases)) {
    const root = join(out, name);
    await mkdir(root, { recursive: true });
    await writeFile(join(root, 'main.ts'), source);
    await writeFile(join(root, 'dep.ts'), 'export interface Widget { value: string }\n');
    let stdout = '', exit = 0;
    await createCli({ REA_LOG_LEVEL: 'silent' }).serve(
      ['import-reference-source', root, '--json'],
      { env: {}, exit: code => { exit = code; }, stdout: text => { stdout += text; } },
    );
    const graph = JSON.parse(stdout);
    await writeFile(join(out, `${name}.json`), stdout);
    console.log(JSON.stringify({ case: name, exit, entries: graph.entries.map(entry => entry.path), relationships: graph.relationships, parse_failures: graph.parse_failures }));
  }
} finally { await closeCliJavaScriptResources(); }

Expected behavior

Retain the relative historical source dependency consistently with the executed type-only declaration control, or report a specific unsupported-coverage limitation rather than silently dropping it. This does not require claiming a runtime load. The official TypeScript import-type reference documents both forms as module type references. REA already records type-only import declarations in its historical dependency extractor; this is separate from application/source-map operations that intentionally omit erased runtime imports.

Impact and scope

Two ordinary valid TypeScript forms lose a dependency from the historical source graph while the source inventory remains available. The final three-case reproduction ran once; earlier application-boundary and source-CLI probes observed the same omission. Real-project prevalence and downstream comparison effects were not measured. The graph is globally partial because of the importer's existing generic filesystem limitation; no complete-global-coverage claim is made. There is no parser-specific diagnostic for this omission. Rewriting the input to the declaration control changes historical source identity and is not a verified unchanged-source workaround.

REA version

Package metadata 6.7.0; immutable main source archive at 96ea32b7e4e28d3c9cd79a1de4b8e1ba3b4244e9. The parent investigation confirmed 6.7.0 as npm latest. These observations concern the stated source runtime, not an executed installed tarball.

Runtime and platform

Node.js v24.22.1 (repository-pinned runtime), existing locked tsx 4.23.15; Linux 7.2.8-9-omarchy x86_64. No native analysis provider was required.

Target and artifact context

Self-authored two-file TypeScript directories. dep.ts declares an interface; main.ts contains one of the three forms shown above. No package manifest, custom loader, symlink, traversal, computed target, or target execution is involved.

Interface

CLI

Earliest failure stage

Artifact or evidence registration

Relevant output or evidence

Observed output from the exact script body:

{"case":"import_type","exit":0,"entries":["dep.ts","main.ts"],"relationships":[],"parse_failures":[]}
{"case":"typeof_import","exit":0,"entries":["dep.ts","main.ts"],"relationships":[],"parse_failures":[]}
{"case":"declaration_control","exit":0,"entries":["dep.ts","main.ts"],"relationships":[{"from_path":"main.ts","to":"dep.ts","kind":"imports","resolution":"internal","parse_state":"parsed"}],"parse_failures":[]}
  • Expected source: official TypeScript syntax documentation, the executed declaration control, and the existing historical import-declaration producer linked above.
  • Failure source: registered public source CLI, including real filesystem traversal, production importer, graph validation and result projection.
  • Evidence provenance: observed.
  • Local verification: reproduced.
  • Reproduction completeness: complete at the public source-CLI boundary. The packaged executable dispatcher and MCP transport were not run.

Public reachability was executed and also traced through command registration and the production importer call. The entry parser and resolver receives no dependency to resolve for the two failing forms.

Typed outcome or recovery expected

A parsed historical imports relationship from main.ts to dep.ts, as in the declaration control, or an explicit scoped limitation if import-type syntax remains unsupported. Keep the original file paths and hashes. This is a proposed restoration check; no fix was implemented.

Analysis or suspected cause

Source-derived: the top-level declaration pass recognizes import declarations and import-equals declarations but does not extract the type alias's module reference. The recursive traversal's collection predicate admits only CallExpression and ImportExpression, omitting Babel TSImportType nodes. The parser invokes those two passes and returns no failure for the omitted form.

Additional context

The supplied open-title corpus was checked before probing. Saved GitHub connector searches covered open and closed issues for TSImportType, import-type, import types, type expression, and broader reference/type combinations. No matching report surfaced; bounded searches cannot prove global absence. Closed #6655 reports exported import-equals declarations. Its larger fixture incidentally contains an import-type file as an independent syntax control, but its claim, reported output, expected behavior and triage concern exported import-equals, a different AST node now handled here. Closed #6654 concerns runtime dynamic imports. Closed #3932 concerns avoiding erased dependencies in a different source-map operation. This report is the historical import-type omission.

AI-assisted research and drafting: Codex. No human-review attestation is made. No source changes, installation, builds, broad test suite, commits, pipeline or issue filing were performed. NOT_ENOUGH_INFO: introducing revision, last known good release, installed-package behavior, other platforms and downstream effects. The draft is frozen after writing; no post-draft bug rerun or second review was performed.

Triage assessment

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: Two valid TypeScript source forms demonstrably lose a historical dependency while a matched declaration control resolves it; source files remain available and no severe downstream incident was established.
  • Workaround status: unverified
  • Workaround basis: The explicit import-type declaration control resolves, but changing the historical input changes its identity. No workflow restoring the original graph while preserving the selected tree was verified.
Originally created by @coygeek on GitHub (Oct 23, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/6859 ## Before submitting - [x] I searched the open and closed issues for a related report. - [x] I removed credentials, tokens, private paths, and sensitive target data. ## Area Evidence, artifacts, or reconstruction ## Observed behavior `import-reference-source` silently omits the dependency in both `export type Widget = import("./dep").Widget` and `export type DepModule = typeof import("./dep")`. Both files are inventoried and hashed, with no parse failures or entry-specific limitations. The equivalent `import type { Widget } from "./dep"` declaration produces the expected parsed internal edge. All three public source-CLI calls exit 0. ## Reproduction From the source checkout identified below, with its locked dependencies already present, save this script as `/tmp/reference-import-types.mjs`. The exact script body ran once with scratch paths through the registered `createCli().serve()` command; the command below normalizes only those temporary paths. It creates inert UTF-8 fixtures and executes REA, never the fixture modules. No build is needed. ```sh node --import ./node_modules/tsx/dist/loader.mjs \ /tmp/reference-import-types.mjs "$PWD" /tmp/reference-import-types-fixtures ``` ```javascript import { mkdir, writeFile } from 'node:fs/promises'; import { join } from 'node:path'; import { pathToFileURL } from 'node:url'; const [repo, out] = process.argv.slice(2); const { createCli, closeCliJavaScriptResources } = await import(pathToFileURL(join(repo, 'src/cli.ts')).href); const cases = { import_type: 'export type Widget = import("./dep").Widget;\n', typeof_import: 'export type DepModule = typeof import("./dep");\n', declaration_control: 'import type { Widget } from "./dep"; export type Output = Widget;\n', }; try { for (const [name, source] of Object.entries(cases)) { const root = join(out, name); await mkdir(root, { recursive: true }); await writeFile(join(root, 'main.ts'), source); await writeFile(join(root, 'dep.ts'), 'export interface Widget { value: string }\n'); let stdout = '', exit = 0; await createCli({ REA_LOG_LEVEL: 'silent' }).serve( ['import-reference-source', root, '--json'], { env: {}, exit: code => { exit = code; }, stdout: text => { stdout += text; } }, ); const graph = JSON.parse(stdout); await writeFile(join(out, `${name}.json`), stdout); console.log(JSON.stringify({ case: name, exit, entries: graph.entries.map(entry => entry.path), relationships: graph.relationships, parse_failures: graph.parse_failures })); } } finally { await closeCliJavaScriptResources(); } ``` ## Expected behavior Retain the relative historical source dependency consistently with the executed type-only declaration control, or report a specific unsupported-coverage limitation rather than silently dropping it. This does not require claiming a runtime load. The [official TypeScript import-type reference](https://www.typescriptlang.org/docs/handbook/modules/reference#import-types) documents both forms as module type references. REA already records type-only import declarations in its [historical dependency extractor](https://github.com/morluto/rea/blob/96ea32b7e4e28d3c9cd79a1de4b8e1ba3b4244e9/src/domain/referenceSourceImportParsing.ts#L182-L190); this is separate from application/source-map operations that intentionally omit erased runtime imports. ## Impact and scope Two ordinary valid TypeScript forms lose a dependency from the historical source graph while the source inventory remains available. The final three-case reproduction ran once; earlier application-boundary and source-CLI probes observed the same omission. Real-project prevalence and downstream comparison effects were not measured. The graph is globally `partial` because of the importer's existing generic filesystem limitation; no complete-global-coverage claim is made. There is no parser-specific diagnostic for this omission. Rewriting the input to the declaration control changes historical source identity and is not a verified unchanged-source workaround. ## REA version Package metadata `6.7.0`; immutable main source archive at `96ea32b7e4e28d3c9cd79a1de4b8e1ba3b4244e9`. The parent investigation confirmed 6.7.0 as npm latest. These observations concern the stated source runtime, not an executed installed tarball. ## Runtime and platform Node.js v24.22.1 (repository-pinned runtime), existing locked tsx 4.23.15; Linux 7.2.8-9-omarchy x86_64. No native analysis provider was required. ## Target and artifact context Self-authored two-file TypeScript directories. `dep.ts` declares an interface; `main.ts` contains one of the three forms shown above. No package manifest, custom loader, symlink, traversal, computed target, or target execution is involved. ## Interface CLI ## Earliest failure stage Artifact or evidence registration ## Relevant output or evidence Observed output from the exact script body: ```jsonl {"case":"import_type","exit":0,"entries":["dep.ts","main.ts"],"relationships":[],"parse_failures":[]} {"case":"typeof_import","exit":0,"entries":["dep.ts","main.ts"],"relationships":[],"parse_failures":[]} {"case":"declaration_control","exit":0,"entries":["dep.ts","main.ts"],"relationships":[{"from_path":"main.ts","to":"dep.ts","kind":"imports","resolution":"internal","parse_state":"parsed"}],"parse_failures":[]} ``` - Expected source: official TypeScript syntax documentation, the executed declaration control, and the existing historical import-declaration producer linked above. - Failure source: registered public source CLI, including real filesystem traversal, production importer, graph validation and result projection. - Evidence provenance: observed. - Local verification: reproduced. - Reproduction completeness: complete at the public source-CLI boundary. The packaged executable dispatcher and MCP transport were not run. Public reachability was executed and also traced through [command registration](https://github.com/morluto/rea/blob/96ea32b7e4e28d3c9cd79a1de4b8e1ba3b4244e9/src/cli/utilityCommands.ts#L279-L342) and the [production importer call](https://github.com/morluto/rea/blob/96ea32b7e4e28d3c9cd79a1de4b8e1ba3b4244e9/src/cli/utilityCommands.ts#L355-L373). The [entry parser and resolver](https://github.com/morluto/rea/blob/96ea32b7e4e28d3c9cd79a1de4b8e1ba3b4244e9/src/application/ReferenceSourceImportEntries.ts#L338-L378) receives no dependency to resolve for the two failing forms. ## Typed outcome or recovery expected A parsed historical `imports` relationship from `main.ts` to `dep.ts`, as in the declaration control, or an explicit scoped limitation if import-type syntax remains unsupported. Keep the original file paths and hashes. This is a proposed restoration check; no fix was implemented. ## Analysis or suspected cause Source-derived: the [top-level declaration pass](https://github.com/morluto/rea/blob/96ea32b7e4e28d3c9cd79a1de4b8e1ba3b4244e9/src/domain/referenceSourceImportParsing.ts#L182-L239) recognizes import declarations and import-equals declarations but does not extract the type alias's module reference. The [recursive traversal's collection predicate](https://github.com/morluto/rea/blob/96ea32b7e4e28d3c9cd79a1de4b8e1ba3b4244e9/src/domain/referenceSourceImportParsing.ts#L250-L260) admits only `CallExpression` and `ImportExpression`, omitting Babel `TSImportType` nodes. The [parser invokes those two passes](https://github.com/morluto/rea/blob/96ea32b7e4e28d3c9cd79a1de4b8e1ba3b4244e9/src/domain/referenceSourceImportParsing.ts#L407-L425) and returns no failure for the omitted form. ## Additional context The supplied open-title corpus was checked before probing. Saved GitHub connector searches covered open and closed issues for `TSImportType`, `import-type`, `import types`, `type expression`, and broader reference/type combinations. No matching report surfaced; bounded searches cannot prove global absence. Closed [#6655](https://github.com/morluto/rea/issues/6655) reports exported import-equals declarations. Its larger fixture incidentally contains an import-type file as an independent syntax control, but its claim, reported output, expected behavior and triage concern exported import-equals, a different AST node now handled here. Closed [#6654](https://github.com/morluto/rea/issues/6654) concerns runtime dynamic imports. Closed [#3932](https://github.com/morluto/rea/issues/3932) concerns avoiding erased dependencies in a different source-map operation. This report is the historical import-type omission. AI-assisted research and drafting: Codex. No human-review attestation is made. No source changes, installation, builds, broad test suite, commits, pipeline or issue filing were performed. NOT_ENOUGH_INFO: introducing revision, last known good release, installed-package behavior, other platforms and downstream effects. The draft is frozen after writing; no post-draft bug rerun or second review was performed. ## Triage assessment - Impact level: P2 - Assessment status: supported - Impact basis: Two valid TypeScript source forms demonstrably lose a historical dependency while a matched declaration control resolves it; source files remain available and no severe downstream incident was established. - Workaround status: unverified - Workaround basis: The explicit import-type declaration control resolves, but changing the historical input changes its identity. No workflow restoring the original graph while preserving the selected tree was verified.
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#4199
No description provided.