Rulebearing Playground Coverage Schemas GitHub

ADR-0040: A TypeScript 7 tsconfig resolves as TypeScript resolves it where dependency-cruiser's tsconfig-paths departs; the tsconfig's module decides what is compiled for acorn

Context

TypeScript 7.0, the native port of the 6.0 compiler, is the latest tag on npm. Its source syntax is 6.0's; what changes for an extractor is the tsconfig. baseUrl is gone, so paths resolve against the folder of the config that declares them, ${configDir} names the folder of the config that is being compiled, and moduleResolution is bundler or one of the node16 to nodenext family. The amd, umd, system and none module kinds, deprecated in 6.0, are removed.

Rulebearing never loads the typescript package. Two things decide whether a TypeScript 7 repository extracts correctly: how oxc_resolver reads the tsconfig, and how the extractor models what dependency-cruiser's transpileModule step hands acorn when tsPreCompilationDeps is off. Both were measured against dependency-cruiser 18.2.0 with TypeScript 6.0.3 (the pin gate 1 records), with the fixtures tests/options/ts7-config and tests/options/ts-config-module. dependency-cruiser cannot load TypeScript 7, whose package does not carry the JavaScript API its transpileModule and tsc parser call; its manifest declares typescript >=2.0.0 <7.0.0.

The compiled module kind. dependency-cruiser spreads the tsconfig's own compilerOptions over its defaults (src/extract/transpile/typescript-wrap.mjs). So module: commonjs, node16, node18, node20 and nodenext, and an unset module with an ES3 or ES5 target, turn every static import and re-export into a require for acorn. commonjs turns import() into one too. export * as ns is lowered to an import only under ES2015 modules, and a tsconfig module overrides the nodenext default of the .mts flavour. The extractor read none of this: it lowered .mts only, whatever the tsconfig said. This was a parity gap, not a decision, and it is closed. TypeScript 7 makes it common, because nodenext is the setting its documentation gives Node projects.

Two resolutions where TypeScript and dependency-cruiser disagree. dependency-cruiser resolves paths through tsconfig-paths-webpack-plugin 4.2.0 and tsconfig-paths 4.2.0:

Case TypeScript 7 (and 6) dependency-cruiser 18.2.0 Cause upstream
paths inherited through extends, no baseUrl (the shape of a TypeScript 7 monorepo's shared base config) against the declaring config's folder; ${configDir} expanded unresolved tsconfig-loader.js rebases an inherited baseUrl but never inherited paths, and does not expand ${configDir}
a bare specifier (src/util.js) that no paths key matches, no baseUrl unresolved (a package lookup) resolved against the tsconfig's folder, type undetermined the plugin calls createMatchPathAsync without addMatchAll, which defaults to true and adds a match-all * whether or not baseUrl is set

oxc_resolver gives TypeScript's answer in both cases. Before TypeScript 7 a repository that used paths almost always set baseUrl, which tsconfig-paths handles correctly, so neither difference was reachable from gate 1's suites or from the layer 5 oracles.

Decision

  1. The tsconfig's module and target decide what acorn reads, as transpileModule decides it upstream: TsCompilerOptions::emit maps them to Es2015, Es2020, Node or CommonJs, and the forms are lowered to match. This is parity and needs no divergence row.
  2. amd, umd, system and none stop the run (exit 2, the tsconfig named, the fix given) when a TypeScript file will be compiled for acorn, that is unless tsPreCompilationDeps is true or the parser is tsc or swc. Their output wraps imports in a loader that the lowering does not model, and upstream's treatment of none differs between .ts and .mts. Answering them as ES modules would be the silent wrong answer the working agreement forbids. TypeScript 7 does not accept them, so no TypeScript 7 repository reaches this error.
  3. Where tsconfig-paths departs from TypeScript, the extractor follows TypeScript, under both configuration formats. The two cases in the table are recorded as permanent rows in conformance/divergences.md for any layer 5 oracle that reaches them, each citing its upstream cause, and the fixtures assert TypeScript's answer with the upstream answer named in their doc comments.

Consequences

Alternatives considered