microsoft/TypeScript
Modules
Imports, exports, dynamic import(), top-level await, declaration vs. side-effect imports, package-export resolution, ESM/CJS interop. The compiler's module story is one of the most-edited areas of the codebase — every Node release that touches modules ripples through here.
Where modules touch the codebase
| Subsystem | File | Role |
|---|---|---|
| Parser | src/compiler/parser.ts |
import/export syntax, import() expressions, import attributes |
| Binder | src/compiler/binder.ts |
externalModuleIndicator, CommonJS detection, module.exports patterns |
| Module resolution | src/compiler/moduleNameResolver.ts + resolutionCache.ts |
import "x" → file path |
| Checker | src/compiler/checker.ts |
Module symbols, export *, type-only imports, dual-package hazards |
| Transformers | src/compiler/transformers/module/ |
CommonJS, SystemJS, ECMAScript module lowering |
| Module specifier generation | src/compiler/moduleSpecifiers.ts |
Inverse of resolution; used by auto-import |
| Emitter | src/compiler/emitter.ts |
Print resolved import shapes |
--module modes
| Mode | Output shape |
|---|---|
none |
Top-level statements only; imports are errors |
commonjs |
require/module.exports |
amd |
define([...], function (...) {}) |
umd |
UMD wrapper supporting both |
system |
SystemJS' System.register API |
es2015 / es2020 / es2022 / esnext |
Native ECMAScript modules with version-appropriate features |
node16 / node18 / node20 / nodenext |
Per-file CJS or ESM based on the file's impliedNodeFormat |
preserve |
Output JSX/import as-is for downstream tooling |
impliedNodeFormat is computed per file from .cts / .mts extensions and the nearest package.json#type. Two files in the same project can be CJS and ESM respectively under node16/nodenext.
Resolution
See systems/module-resolution. The high-level summary:
- Apply
pathsaliases. - For relative imports, walk extensions. For bare names, walk
node_modules/. - Consult
package.jsonexports/imports/types/typingsfor the right entry. - Apply conditional-export conditions (
import/require/types/default/etc.) based on the resolution mode and the importing file's format.
Type-only imports / exports
import type { X } from "./mod" and export type { Y } are stripped by the transformer — they have no runtime effect. The parser tracks isTypeOnly flags on import/export specifiers; the emitter elides them. --isolatedModules and --verbatimModuleSyntax are the strictness knobs.
CommonJS interop
require() calls in .ts source are not the same thing as import statements. When --esModuleInterop is on, the emitter:
- Inserts
__importStar/__importDefaulthelpers around CJS imports so default-imports see the module's default export (per spec) rather than the whole CJS namespace. - Emits an
Object.defineProperty(exports, "__esModule", { value: true })marker.
For node16/nodenext, additional checks live in the checker:
- A
.mts(ESM) file importing a.cjs(CJS) module gets adefaultimport that's the CJS namespace. - A
.cjsfile importing a.mtsmodule is an error unless the import is a dynamicimport(). - Re-exports across formats are validated to avoid dual-package hazards.
import attributes / import assertions
import x from "./y" with { type: "json" } is an ES2025 feature TypeScript supports. Parsed into the attributes field on ImportDeclaration. Currently has limited semantic effect inside TypeScript itself — type-aware JSON imports come from resolveJsonModule.
Module transformers
Each output mode has its own transformer in src/compiler/transformers/module/. They share a base that handles imports, exports, hoisting, and the __exportStar helper. The most subtle is transformSystemModule (used for SystemJS): it rewrites the entire module body into a System.register call with explicit setters and execute phases so cyclic imports work.
transformImpliedNodeFormatDependentModule is the dispatcher for node16/nodenext — it inspects each source file's impliedNodeFormat and delegates to either the CommonJS or ECMAScript module transformer per file.
Auto-import
The language service auto-imports symbols when a completion is selected. The path it inserts comes from src/compiler/moduleSpecifiers.ts, which inverts the resolution rules:
- Honours
pathsso users see their alias rather than a deep relative path. - Prefers
node_modulespackage names when the file is published. - Respects
package.json#exportsso the suggestion is a publicly-importable entry, not an internal module. - Falls back to relative paths.
Tests
Module tests are spread over many tests/cases/conformance/ subdirectories:
externalModules/,internalModules/,moduleResolution/,dynamicImport/,importDefer/,importAssertion/,importAttributes/,node/(Node-style).tests/cases/conformance/salsa/covers JS-with-types module patterns.
Entry points for modification
- New module mode — extend
ModuleKindinsrc/compiler/types.ts, wire it intogetModuleTransformerintransformer.ts, add a transformer file undertransformers/module/. - Resolution rules — see systems/module-resolution.
- Conditional
exports/importssemantics —getConditionsandtryGetConditionsFromBundlerOptionsinmoduleNameResolver.ts.
See systems/module-resolution and systems/transformers for the underpinnings.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.