├── .gitignore ├── moduleResolution ├── Node │ ├── 6-mixed-ts-js │ │ ├── file.js │ │ ├── file.ts │ │ ├── package.json │ │ ├── tsconfig.json │ │ ├── example.ts │ │ └── README.md │ ├── 3-package │ │ ├── node_modules │ │ │ ├── pkg │ │ │ │ ├── file.js │ │ │ │ ├── file.ts │ │ │ │ ├── file.d.ts │ │ │ │ └── package.json │ │ │ └── pkg-only-main │ │ │ │ ├── file.js │ │ │ │ ├── file.ts │ │ │ │ ├── file.d.ts │ │ │ │ └── package.json │ │ ├── tsconfig1.json │ │ ├── tsconfig2.json │ │ ├── example1.ts │ │ ├── example2.ts │ │ ├── package.json │ │ └── README.md │ ├── 1-relative │ │ ├── file.ts │ │ ├── tsconfig.json │ │ ├── package.json │ │ ├── example.ts │ │ └── README.md │ ├── 4-index │ │ ├── subfolder │ │ │ ├── index.d.ts │ │ │ ├── index.ts │ │ │ └── index.js │ │ ├── tsconfig.json │ │ ├── package.json │ │ ├── example.ts │ │ └── README.md │ ├── 7-typesVersions │ │ ├── node_modules │ │ │ └── pkg │ │ │ │ ├── project.js │ │ │ │ ├── project.d.ts │ │ │ │ ├── project.ts │ │ │ │ ├── project.v4.d.ts │ │ │ │ └── package.json │ │ ├── package.json │ │ ├── example.ts │ │ ├── tsconfig.json │ │ └── README.md │ ├── 2-non-relative │ │ ├── node_modules │ │ │ └── pkg │ │ │ │ ├── file.d.ts │ │ │ │ └── file.js │ │ ├── tsconfig.json │ │ ├── package.json │ │ ├── example.ts │ │ └── README.md │ ├── 5-javascript │ │ ├── file.js │ │ ├── tsconfig.json │ │ ├── example.ts │ │ ├── tsconfig-allowjs.json │ │ ├── package.json │ │ └── README.md │ ├── tsconfig.json │ └── README.md ├── Node16 │ ├── 1-relative │ │ ├── target.mts │ │ ├── target.ts │ │ ├── example-esm.mts │ │ ├── example-cjs.ts │ │ ├── example-extensionless.mts │ │ ├── package.json │ │ ├── tsconfig-cjs.json │ │ ├── tsconfig-esm.json │ │ ├── tsconfig-extensionless.json │ │ └── README.md │ ├── 2-export-map │ │ ├── node_modules │ │ │ └── pkg │ │ │ │ ├── index.d.ts │ │ │ │ ├── index.ts │ │ │ │ ├── index.cts │ │ │ │ ├── index.d.cts │ │ │ │ ├── index.cjs │ │ │ │ ├── index.js │ │ │ │ ├── tsconfig.json │ │ │ │ └── package.json │ │ ├── example1-static │ │ │ ├── package.json │ │ │ ├── static.cts │ │ │ ├── static.mts │ │ │ └── tsconfig.json │ │ ├── example2-dynamic │ │ │ ├── dynamic.cts │ │ │ ├── dynamic.mts │ │ │ ├── package.json │ │ │ └── tsconfig.json │ │ ├── example3-mixed │ │ │ ├── package.json │ │ │ ├── mixed.d.cts │ │ │ ├── mixed.cts │ │ │ └── tsconfig.json │ │ └── README.md │ └── README.md └── README.md ├── misc └── ambient-modules │ ├── 2-typeRoots │ ├── node_modules │ │ └── js-pkg │ │ │ ├── index.js │ │ │ └── package.json │ ├── typings │ │ └── external.d.ts │ ├── package.json │ ├── example.ts │ └── tsconfig.json │ └── 1-definitely-typed │ ├── node_modules │ ├── js-pkg │ │ ├── index.js │ │ └── package.json │ └── @types │ │ └── js-pkg │ │ ├── index.d.ts │ │ └── package.json │ ├── package.json │ ├── example.ts │ └── tsconfig.json ├── package.json └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | .vscode 3 | dist/ 4 | -------------------------------------------------------------------------------- /moduleResolution/Node/6-mixed-ts-js/file.js: -------------------------------------------------------------------------------- 1 | export const value = "js"; 2 | -------------------------------------------------------------------------------- /moduleResolution/Node/3-package/node_modules/pkg/file.js: -------------------------------------------------------------------------------- 1 | exports.value = ".js"; 2 | -------------------------------------------------------------------------------- /moduleResolution/Node/6-mixed-ts-js/file.ts: -------------------------------------------------------------------------------- 1 | export const value = "ts" as const; 2 | -------------------------------------------------------------------------------- /moduleResolution/Node/1-relative/file.ts: -------------------------------------------------------------------------------- 1 | export const value = "relative" as const; 2 | -------------------------------------------------------------------------------- /moduleResolution/Node/3-package/node_modules/pkg-only-main/file.js: -------------------------------------------------------------------------------- 1 | exports.value = ".js"; 2 | -------------------------------------------------------------------------------- /moduleResolution/Node/4-index/subfolder/index.d.ts: -------------------------------------------------------------------------------- 1 | export declare const value: "index"; 2 | -------------------------------------------------------------------------------- /moduleResolution/Node/4-index/subfolder/index.ts: -------------------------------------------------------------------------------- 1 | export const value = "index" as const; 2 | -------------------------------------------------------------------------------- /moduleResolution/Node16/1-relative/target.mts: -------------------------------------------------------------------------------- 1 | export const value = 'target-esm' as const; 2 | -------------------------------------------------------------------------------- /moduleResolution/Node16/1-relative/target.ts: -------------------------------------------------------------------------------- 1 | export const value = 'target-cjs' as const; 2 | -------------------------------------------------------------------------------- /misc/ambient-modules/2-typeRoots/node_modules/js-pkg/index.js: -------------------------------------------------------------------------------- 1 | export const value = 'js-pkg'; 2 | -------------------------------------------------------------------------------- /moduleResolution/Node/3-package/node_modules/pkg/file.ts: -------------------------------------------------------------------------------- 1 | export const value = ".ts" as const; 2 | -------------------------------------------------------------------------------- /moduleResolution/Node/7-typesVersions/node_modules/pkg/project.js: -------------------------------------------------------------------------------- 1 | export const value = "js"; 2 | -------------------------------------------------------------------------------- /misc/ambient-modules/1-definitely-typed/node_modules/js-pkg/index.js: -------------------------------------------------------------------------------- 1 | export const value = 'js-pkg'; 2 | -------------------------------------------------------------------------------- /moduleResolution/Node/3-package/node_modules/pkg/file.d.ts: -------------------------------------------------------------------------------- 1 | export declare const value: ".d.ts"; 2 | -------------------------------------------------------------------------------- /moduleResolution/Node/2-non-relative/node_modules/pkg/file.d.ts: -------------------------------------------------------------------------------- 1 | export declare const value: "file.d.ts"; 2 | -------------------------------------------------------------------------------- /moduleResolution/Node/3-package/node_modules/pkg-only-main/file.ts: -------------------------------------------------------------------------------- 1 | export const value = ".ts" as const; 2 | -------------------------------------------------------------------------------- /moduleResolution/Node/7-typesVersions/node_modules/pkg/project.d.ts: -------------------------------------------------------------------------------- 1 | export declare const value: "d.ts"; 2 | -------------------------------------------------------------------------------- /moduleResolution/Node/7-typesVersions/node_modules/pkg/project.ts: -------------------------------------------------------------------------------- 1 | export const value = "ts" as const; 2 | -------------------------------------------------------------------------------- /moduleResolution/Node/3-package/node_modules/pkg-only-main/file.d.ts: -------------------------------------------------------------------------------- 1 | export declare const value: ".d.ts"; 2 | -------------------------------------------------------------------------------- /moduleResolution/Node/3-package/node_modules/pkg-only-main/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "./file.js" 3 | } 4 | -------------------------------------------------------------------------------- /moduleResolution/Node/7-typesVersions/node_modules/pkg/project.v4.d.ts: -------------------------------------------------------------------------------- 1 | export declare const value: "v4.d.ts"; 2 | -------------------------------------------------------------------------------- /moduleResolution/Node/5-javascript/file.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @type {string | boolean} 3 | */ 4 | export const value = "js"; 5 | -------------------------------------------------------------------------------- /misc/ambient-modules/1-definitely-typed/node_modules/@types/js-pkg/index.d.ts: -------------------------------------------------------------------------------- 1 | export declare const value: "js-pkg-d-ts"; 2 | -------------------------------------------------------------------------------- /misc/ambient-modules/2-typeRoots/node_modules/js-pkg/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "js-pkg", 3 | "main": "index.js" 4 | } 5 | -------------------------------------------------------------------------------- /misc/ambient-modules/2-typeRoots/typings/external.d.ts: -------------------------------------------------------------------------------- 1 | declare module "js-pkg" { 2 | const value: "js-pkg-types"; 3 | } 4 | -------------------------------------------------------------------------------- /moduleResolution/Node/1-relative/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "include": ["example.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /moduleResolution/Node/3-package/tsconfig1.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "include": ["example1.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /moduleResolution/Node/3-package/tsconfig2.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "include": ["example2.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /moduleResolution/Node/4-index/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "include": ["example.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /misc/ambient-modules/1-definitely-typed/node_modules/js-pkg/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "js-pkg", 3 | "main": "index.js" 4 | } 5 | -------------------------------------------------------------------------------- /misc/ambient-modules/2-typeRoots/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "build": "tsc -p tsconfig.json --outDir dist" 4 | } 5 | } -------------------------------------------------------------------------------- /moduleResolution/Node/2-non-relative/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "include": ["example.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /moduleResolution/Node/3-package/node_modules/pkg/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "./file.js", 3 | "types": "./file.d.ts" 4 | } 5 | -------------------------------------------------------------------------------- /moduleResolution/Node/5-javascript/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "include": ["example.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /moduleResolution/Node/1-relative/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "build": "npx tsc -p tsconfig.json --outDir dist" 4 | } 5 | } -------------------------------------------------------------------------------- /moduleResolution/Node/4-index/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "build": "npx tsc -p tsconfig.json --outDir dist" 4 | } 5 | } -------------------------------------------------------------------------------- /moduleResolution/Node16/2-export-map/node_modules/pkg/index.d.ts: -------------------------------------------------------------------------------- 1 | export declare const value: "esm"; 2 | export type Type = "type-esm"; 3 | -------------------------------------------------------------------------------- /moduleResolution/Node16/2-export-map/node_modules/pkg/index.ts: -------------------------------------------------------------------------------- 1 | export const value = "esm" as const; 2 | export type Type = "type-esm"; 3 | -------------------------------------------------------------------------------- /misc/ambient-modules/1-definitely-typed/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "build": "tsc -p tsconfig.json --outDir dist" 4 | } 5 | } -------------------------------------------------------------------------------- /moduleResolution/Node/2-non-relative/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "build": "npx tsc -p tsconfig.json --outDir dist" 4 | } 5 | } -------------------------------------------------------------------------------- /moduleResolution/Node/3-package/example1.ts: -------------------------------------------------------------------------------- 1 | import { value } from "pkg"; 2 | 3 | export function Package1() { 4 | return value; 5 | } 6 | -------------------------------------------------------------------------------- /moduleResolution/Node/6-mixed-ts-js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "build": "npx tsc -p tsconfig.json --outDir dist" 4 | } 5 | } -------------------------------------------------------------------------------- /moduleResolution/Node/7-typesVersions/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "build": "npx tsc -p tsconfig.json --outDir dist" 4 | } 5 | } -------------------------------------------------------------------------------- /moduleResolution/Node16/2-export-map/node_modules/pkg/index.cts: -------------------------------------------------------------------------------- 1 | export const value = "cjs" as const; 2 | export type Type = "type-cjs"; 3 | -------------------------------------------------------------------------------- /moduleResolution/Node16/2-export-map/node_modules/pkg/index.d.cts: -------------------------------------------------------------------------------- 1 | export declare const value: "cjs"; 2 | export type Type = "type-cjs"; 3 | -------------------------------------------------------------------------------- /misc/ambient-modules/2-typeRoots/example.ts: -------------------------------------------------------------------------------- 1 | import { value } from 'js-pkg'; 2 | 3 | export function TypeRoots() { 4 | return value; 5 | } 6 | -------------------------------------------------------------------------------- /moduleResolution/Node/3-package/example2.ts: -------------------------------------------------------------------------------- 1 | import { value } from "pkg-only-main"; 2 | 3 | export function Package2() { 4 | return value; 5 | } 6 | -------------------------------------------------------------------------------- /moduleResolution/Node/5-javascript/example.ts: -------------------------------------------------------------------------------- 1 | import { value } from "./file"; 2 | 3 | export function JavaScriptImport() { 4 | return value; 5 | } 6 | -------------------------------------------------------------------------------- /moduleResolution/Node/7-typesVersions/example.ts: -------------------------------------------------------------------------------- 1 | import { value } from "pkg"; 2 | 3 | export function TypesVersions() { 4 | return value; 5 | } 6 | -------------------------------------------------------------------------------- /moduleResolution/Node16/2-export-map/example1-static/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "build": "tsc -p tsconfig.json --outDir dist" 4 | } 5 | } -------------------------------------------------------------------------------- /moduleResolution/Node16/2-export-map/example2-dynamic/dynamic.cts: -------------------------------------------------------------------------------- 1 | export async function Dynamic() { 2 | return (await import("pkg")).value; 3 | } 4 | -------------------------------------------------------------------------------- /moduleResolution/Node16/2-export-map/example2-dynamic/dynamic.mts: -------------------------------------------------------------------------------- 1 | export async function Dynamic() { 2 | return (await import("pkg")).value; 3 | } 4 | -------------------------------------------------------------------------------- /moduleResolution/Node16/2-export-map/example3-mixed/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "build": "tsc -p tsconfig.json --outDir dist" 4 | } 5 | } -------------------------------------------------------------------------------- /misc/ambient-modules/1-definitely-typed/example.ts: -------------------------------------------------------------------------------- 1 | import { value } from 'js-pkg'; 2 | 3 | export function DefinitelyTyped() { 4 | return value; 5 | } 6 | -------------------------------------------------------------------------------- /moduleResolution/Node/4-index/subfolder/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | exports.__esModule = true; 3 | exports.value = void 0; 4 | exports.value = "index"; 5 | -------------------------------------------------------------------------------- /moduleResolution/Node16/2-export-map/example1-static/static.cts: -------------------------------------------------------------------------------- 1 | import {value} from 'pkg'; 2 | 3 | export function Static() { 4 | return value; 5 | } 6 | -------------------------------------------------------------------------------- /moduleResolution/Node16/2-export-map/example1-static/static.mts: -------------------------------------------------------------------------------- 1 | import {value} from 'pkg'; 2 | 3 | export function Static() { 4 | return value; 5 | } 6 | -------------------------------------------------------------------------------- /moduleResolution/Node16/2-export-map/example2-dynamic/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "build": "tsc -p tsconfig.json --outDir dist" 4 | } 5 | } -------------------------------------------------------------------------------- /moduleResolution/Node16/2-export-map/example3-mixed/mixed.d.cts: -------------------------------------------------------------------------------- 1 | export declare function Static(): "cjs"; 2 | export declare function Dynamic(): Promise<"esm">; 3 | -------------------------------------------------------------------------------- /misc/ambient-modules/1-definitely-typed/node_modules/@types/js-pkg/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@types/js-pkg", 3 | "main": "", 4 | "types": "index.d.ts" 5 | } 6 | -------------------------------------------------------------------------------- /moduleResolution/Node16/2-export-map/node_modules/pkg/index.cjs: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | exports.__esModule = true; 3 | exports.value = void 0; 4 | exports.value = "cjs"; 5 | -------------------------------------------------------------------------------- /moduleResolution/Node16/2-export-map/node_modules/pkg/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | exports.__esModule = true; 3 | exports.value = void 0; 4 | exports.value = "esm"; 5 | -------------------------------------------------------------------------------- /moduleResolution/Node/2-non-relative/node_modules/pkg/file.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | exports.__esModule = true; 3 | exports.value = void 0; 4 | exports.value = "file.js"; 5 | -------------------------------------------------------------------------------- /moduleResolution/Node/6-mixed-ts-js/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "allowJs": true, 5 | }, 6 | "include": ["example.ts"] 7 | } 8 | -------------------------------------------------------------------------------- /moduleResolution/Node/7-typesVersions/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "allowJs": true, 5 | }, 6 | "include": ["example.ts"] 7 | } 8 | -------------------------------------------------------------------------------- /moduleResolution/Node/5-javascript/tsconfig-allowjs.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "allowJs": true, 5 | }, 6 | "include": ["example.ts"] 7 | } 8 | -------------------------------------------------------------------------------- /moduleResolution/Node/3-package/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "build1": "npx tsc -p tsconfig1.json --outDir dist", 4 | "build2": "npx tsc -p tsconfig2.json --outDir dist" 5 | } 6 | } -------------------------------------------------------------------------------- /moduleResolution/Node/6-mixed-ts-js/example.ts: -------------------------------------------------------------------------------- 1 | // allowJs is enabled. Where will this resolve? 2 | import { value } from "./file.js"; 3 | 4 | export function Mixed() { 5 | return value; 6 | } 7 | -------------------------------------------------------------------------------- /moduleResolution/Node/1-relative/example.ts: -------------------------------------------------------------------------------- 1 | // This is a relative import because it starts with "./" 2 | import { value } from "./file"; 3 | 4 | export function Relative() { 5 | return value; 6 | } 7 | -------------------------------------------------------------------------------- /moduleResolution/Node16/1-relative/example-esm.mts: -------------------------------------------------------------------------------- 1 | // Note that the final, compiled extension is used (.mjs) 2 | import { value } from './target.mjs' 3 | 4 | export function ESM() { 5 | return value; 6 | } 7 | -------------------------------------------------------------------------------- /moduleResolution/Node/5-javascript/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "build": "npx tsc -p tsconfig.json --outDir dist", 4 | "build-allowjs": "npx tsc -p tsconfig-allowjs.json --outDir dist" 5 | } 6 | } -------------------------------------------------------------------------------- /moduleResolution/Node/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "node", 4 | "declaration": true, 5 | "emitDeclarationOnly": true, 6 | "traceResolution": true 7 | }, 8 | } 9 | -------------------------------------------------------------------------------- /moduleResolution/Node/4-index/example.ts: -------------------------------------------------------------------------------- 1 | // This is not specifying a path to the file, `index` will be automatically used 2 | import { value } from "./subfolder"; 3 | 4 | export function Index() { 5 | return value; 6 | } 7 | -------------------------------------------------------------------------------- /moduleResolution/Node16/1-relative/example-cjs.ts: -------------------------------------------------------------------------------- 1 | // This file is not an ES module, so it's fine not to specify the extension 2 | import { value } from './target' 3 | 4 | export function CJS() { 5 | return value; 6 | } 7 | -------------------------------------------------------------------------------- /moduleResolution/Node16/2-export-map/node_modules/pkg/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "Node16", 4 | "declaration": true 5 | }, 6 | "include": ["index.ts", "index.cts"] 7 | } 8 | -------------------------------------------------------------------------------- /misc/ambient-modules/1-definitely-typed/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "declaration": true, 4 | "emitDeclarationOnly": true, 5 | "traceResolution": true 6 | }, 7 | "include": ["example.ts"] 8 | } 9 | -------------------------------------------------------------------------------- /moduleResolution/Node/7-typesVersions/node_modules/pkg/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pkg", 3 | "main": "./project.js", 4 | "types": "./project.d.ts", 5 | "typesVersions": { 6 | ">=4.0": { "*.d.ts": ["*.v4.d.ts"] } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /moduleResolution/Node16/1-relative/example-extensionless.mts: -------------------------------------------------------------------------------- 1 | // This will not resolve, and will not generate valid type definitions 2 | import { value } from './target' 3 | 4 | export function Extensionless() { 5 | return value; 6 | } 7 | -------------------------------------------------------------------------------- /misc/ambient-modules/2-typeRoots/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "typeRoots": ["node_modules/@types", "./typings"], 4 | "declaration": true, 5 | "emitDeclarationOnly": true, 6 | "traceResolution": true 7 | }, 8 | } 9 | -------------------------------------------------------------------------------- /moduleResolution/Node16/2-export-map/example3-mixed/mixed.cts: -------------------------------------------------------------------------------- 1 | import {value} from 'pkg'; 2 | 3 | export function Static() { 4 | return value; 5 | } 6 | 7 | export async function Dynamic() { 8 | return (await import("pkg")).value; 9 | } 10 | -------------------------------------------------------------------------------- /moduleResolution/Node16/1-relative/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "build-cjs": "tsc -p tsconfig-cjs.json --outDir dist", 4 | "build-esm": "tsc -p tsconfig-esm.json --outDir dist", 5 | "build-extensionless": "tsc -p tsconfig-extensionless.json --outDir dist" 6 | } 7 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ts-module-resolution-example-node", 3 | "version": "1.0.0", 4 | "description": "Examples of TypeScript module resolution", 5 | "author": "Ian VanSchooten", 6 | "license": "MIT", 7 | "devDependencies": { 8 | "typescript": "^4.9.3" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /moduleResolution/Node16/1-relative/tsconfig-cjs.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "NodeNext", 4 | "target": "ESNext", 5 | "module": "NodeNext", 6 | "declaration": true, 7 | "emitDeclarationOnly": true, 8 | "traceResolution": true 9 | }, 10 | "include": ["example-cjs.ts"] 11 | } 12 | -------------------------------------------------------------------------------- /moduleResolution/Node16/1-relative/tsconfig-esm.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "NodeNext", 4 | "target": "ESNext", 5 | "module": "NodeNext", 6 | "declaration": true, 7 | "emitDeclarationOnly": true, 8 | "traceResolution": true 9 | }, 10 | "include": ["example-esm.mts"] 11 | } 12 | -------------------------------------------------------------------------------- /moduleResolution/Node16/2-export-map/example3-mixed/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "NodeNext", 4 | "target": "ESNext", 5 | "module": "ESNext", 6 | "declaration": true, 7 | "emitDeclarationOnly": true, 8 | "traceResolution": true 9 | }, 10 | "include": ["mixed.cts"] 11 | } 12 | -------------------------------------------------------------------------------- /moduleResolution/Node16/1-relative/tsconfig-extensionless.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "NodeNext", 4 | "target": "ESNext", 5 | "module": "NodeNext", 6 | "declaration": true, 7 | "emitDeclarationOnly": true, 8 | "traceResolution": true 9 | }, 10 | "include": ["example-extensionless.mts"] 11 | } 12 | -------------------------------------------------------------------------------- /moduleResolution/Node16/2-export-map/example1-static/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "NodeNext", 4 | "target": "ESNext", 5 | "module": "NodeNext", 6 | "declaration": true, 7 | "emitDeclarationOnly": true, 8 | "traceResolution": true 9 | }, 10 | "include": ["static.cts", "static.mts"] 11 | } 12 | -------------------------------------------------------------------------------- /moduleResolution/Node16/2-export-map/example2-dynamic/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "NodeNext", 4 | "target": "ESNext", 5 | "module": "ESNext", 6 | "declaration": true, 7 | "emitDeclarationOnly": true, 8 | "traceResolution": true 9 | }, 10 | "include": ["dynamic.cts", "dynamic.mts"] 11 | } 12 | -------------------------------------------------------------------------------- /moduleResolution/Node/6-mixed-ts-js/README.md: -------------------------------------------------------------------------------- 1 | # Experiment: Import from JavaScript file, when .ts file also exists 2 | 3 | ## Example 4 | 5 | This example demonstrates what happens if you try to import from a JavaScript file, 6 | with `allowJs` enabled, but there is a TypeScript file of the same name. 7 | 8 | Based on what you've learned from the other examples, take a guess at what will happen, 9 | then run `npm run build` to find out. 10 | -------------------------------------------------------------------------------- /moduleResolution/Node16/2-export-map/node_modules/pkg/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pkg", 3 | "type": "module", 4 | "exports": { 5 | ".": { 6 | "import": { 7 | "types": "./index.d.ts", 8 | "default": "./index.js" 9 | }, 10 | "require": { 11 | "types": "./index.d.cts", 12 | "default": "./index.cjs" 13 | } 14 | }, 15 | "./package.json": "./package.json" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /moduleResolution/Node/2-non-relative/example.ts: -------------------------------------------------------------------------------- 1 | // This is a non-relative import. TS knows to look in `node_modules` 2 | import { value } from "pkg/file"; 3 | // This is not in the "node_modules" within this folder, it's up a few levels, 4 | // but TypeScript will find it. 5 | import * as ts from "typescript"; 6 | 7 | export function NonRelativePkg() { 8 | return value; 9 | } 10 | 11 | export function NonRelativeTS() { 12 | return ts; 13 | } 14 | -------------------------------------------------------------------------------- /moduleResolution/Node/4-index/README.md: -------------------------------------------------------------------------------- 1 | # TypeScript module resolution for index files 2 | 3 | Node.js treats files named `index.js` specially, and allows requiring them by folder name alone, 4 | so instead of `require("./subfolder/index")`, it's possible to use `require("./subfolder")`. 5 | 6 | When using `--moduleResolution node`, TypeScript will also look for files named `index.ts`, `index.tsx`, or `index.d.ts` (in that order). 7 | 8 | ## Example 9 | 10 | The example here (`"example.ts"`) imports from a subfolder containing an `index.ts` file, which will be correctly resolved by TypeScript. 11 | 12 | You can run `npm run build` to see a trace of the module resolution and generate a type definition file. 13 | -------------------------------------------------------------------------------- /moduleResolution/Node/5-javascript/README.md: -------------------------------------------------------------------------------- 1 | # TypeScript module resolution with `allowJs` 2 | 3 | TypeScript can infer some types and use JSDoc annotations when importing JavaScript files 4 | if the `--allowJs` [option](https://www.typescriptlang.org/tsconfig#allowJs) is used. 5 | 6 | ## Examples 7 | 8 | The `example.ts` file here imports from `file`, which maps to the javascript file `file.js`. 9 | There are two different tcsonfig files to demonstrate the effect of the `allowJs` setting. 10 | 11 | ### Without allowJs 12 | 13 | If you run `npm run build` in this directory, `allowJs` will be `false`, and while the build will 14 | succeed (contrary to the docs?), the type of the imported `value` will be `any`. 15 | 16 | ### With allowJs 17 | 18 | However, if you run `npm run build-allowjs`, the `allowJs` setting will be `true` 19 | (nothing else is changed), and you can see that the JSDoc annotations are used to 20 | create a `file.d.ts` declaration file, and the emitted `example.d.ts` has the correct type. 21 | 22 | This can be one approach to adding types to a javascript project, by adding JSDoc comments. 23 | -------------------------------------------------------------------------------- /moduleResolution/Node/1-relative/README.md: -------------------------------------------------------------------------------- 1 | # TypeScript module resolution for relative imports 2 | 3 | One of the simplest kinds of imports to resolve, "relative" imports provide a direct path from 4 | the file doing the importing to the module being imported. They start with either a `"/"` 5 | (a path relative to the filesystem root), a `"./"` (a path to a module inside the importing 6 | file's directory), or a `"../"` (a path in an ancestor directory). 7 | 8 | When using `--moduleResolution node`, an explicit extension can optionally be used, 9 | but is not required. 10 | 11 | Relative imports are normally used for files within your own project. 12 | 13 | ## Example 14 | 15 | This example demonstrates a simple, direct relative import. There are other approaches Node.js (and TypeScript) can take if it doesn't find a file at the specified path, but these will be explored in other examples. 16 | 17 | In this case, `example.ts` imports directly from `"./file"`. Note the extension is not required. TypeScript resolves this import to the module at `"./file.ts"`. 18 | 19 | You can see the module resolution process (the "trace") by running `npm run build` in this example folder. Running this command will also create type definition files (.d.ts) which you can examine. 20 | -------------------------------------------------------------------------------- /moduleResolution/README.md: -------------------------------------------------------------------------------- 1 | # `moduleResolution` 2 | 3 | [Config reference](https://www.typescriptlang.org/tsconfig#moduleResolution) 4 | 5 | This is the most direct way to change TypeScript's module resolution mode. It has three current possible values, 6 | and more are being developed to better meet the needs of bundlers and browser-native modules. 7 | 8 | The currently-supported values are (as of TS 4.9): 9 | 10 | - `"Classic"` 11 | - `"Node"` 12 | - `"Node16"` or `"NodeNext"` (currently these mean the same thing) 13 | 14 | "Classic" mode is rarely used in modern projects, and is not explored in this repository. For details on how it works, 15 | see the TypeScript [module resolution guide](https://www.typescriptlang.org/docs/handbook/module-resolution.html#classic). 16 | 17 | "Node" is likely the most commonly-used option, and mimics the behavior of Node 11 and below. 18 | See the [Node](Node/) directory for examples and explanations. 19 | 20 | "NodeNext" is intended to evolve as Node.js adds new versions and new features, but for now it is the same as "Node16", 21 | which adds support for the Node.js style of ES modules. There are some differences in TypeScript Module resolution 22 | compared to "Node", and these are explored in the [Node16](Node16/) directory. See the [README](Node16/README.md) there to begin. 23 | -------------------------------------------------------------------------------- /moduleResolution/Node/2-non-relative/README.md: -------------------------------------------------------------------------------- 1 | # TypeScript module resolution for non-relative imports 2 | 3 | When using `--moduleResolution node`, imports without a `"/"`, `"./"`, or `"../"` (so called 4 | "non-relative" imports) use the Node.js convention of looking inside special 5 | folders named "node_modules". 6 | TypeScript will start in the same directory as the file performing the import, and then walk up 7 | the directory structure looking through each "node_modules" folder it finds until it locates 8 | the module being imported. 9 | 10 | Check out the typescript [module resolution documentation](https://www.typescriptlang.org/docs/handbook/module-resolution.html#how-nodejs-resolves-modules) 11 | for more details on the process it follows. 12 | 13 | ## Example 14 | 15 | The simple example shown in this folder demonstrates a non-relative import in `example.ts`. 16 | TypeScript resolves the module to `node_modules/pkg/file.d.ts`. 17 | It also imports from `"typescript"`, which is located in the node_modules folder at the root 18 | of this repository. 19 | 20 | You can run `npm run build` in this folder to see a trace of the module resolution 21 | and generate a type definition file. You'll notice that TypeScript looks for node_modules folders up 22 | the directory tree until it finds what it is looking for. 23 | -------------------------------------------------------------------------------- /moduleResolution/Node/7-typesVersions/README.md: -------------------------------------------------------------------------------- 1 | ## The use of `typesVersions` for TypeScript module resolution 2 | 3 | Package authors sometimes want to publish different type definitions to be used in different 4 | versions of TypeScript. For example, if new syntax allows for more thorough typing, 5 | but is not supported in older versions of TypeScript, they may want to publish two definition files, 6 | one with the old syntax, and one with the new. The way they can accomplish this is by adding a 7 | `typesVersions` field to their package.json file. 8 | 9 | [Introduced](https://devblogs.microsoft.com/typescript/announcing-typescript-3-1/#version-redirects-for-typescript-via-typesversions) in TypeScript 3.1, `typesVersions` is a powerful way to change TypeScript's module resolution. 10 | 11 | ## Example 12 | 13 | This example includes a package named `pkg` using `typesVersions` in its package.json: 14 | 15 | ```json 16 | { 17 | "name": "pkg", 18 | "main": "./project.js", 19 | "types": "./project.d.ts", 20 | "typesVersions": { 21 | ">=4.0": { "*.d.ts": ["*.v4.d.ts"] } 22 | } 23 | } 24 | ``` 25 | 26 | This means that any version of TypeScript that is 4.0 or higher, when requesting a file ending in `.d.ts`, will resolve instead to a file with the same name ending in `.v4.d.ts`. Versions of TypeScript less than this (including versions that don't support `typesVersions`), will receive the fallback specified in the `"types"` field. 27 | 28 | Run `npm run build` in this folder to generate `example.d.ts` and see the module resolution trace. 29 | -------------------------------------------------------------------------------- /moduleResolution/Node16/README.md: -------------------------------------------------------------------------------- 1 | # moduleResolution: "Node16" / "NodeNext" 2 | 3 | Typescript 4.7 added two new `moduleResolution` values, "Node16" and "NodeNext". 4 | "NodeNext" is currently the same as "Node16", but is intended to change if/when 5 | Node.js changes behavior in future versions. 6 | 7 | The `"Node16"` mode is intended to mimic Node.js module resolution behavior from Node.js 12 onward, 8 | especially when it comes to the way it treats ES modules. Node.js native ESM handling has a few differences 9 | from CommonJs and new restrictions on relative import specifiers. It is also more restrictive than most bundlers. 10 | If you are using a bundler to compile your code, you may be best off using `"moduleResolution": "node"`, 11 | until new modes are introduced which will better support bundler usage. 12 | 13 | ## Examples 14 | 15 | This repo contains examples demonstrating the major changes with "Node16" module resolution in TypeScript. 16 | 17 | 18 | ### Relative imports 19 | 20 | When using ES modules (by setting `"type": "module"` in your package.json file, or naming your TypeScript files with a `.mts` extension), "Node16" module resolution for relative imports have a couple of differences from "Node". 21 | The folder `/1-relative` provides an example of a relative import, and you can learn about the differences in the [readme](1-relative/README.md). 22 | 23 | ### Non-relative imports 24 | 25 | In `"Node16"` resolution mode, TypeScript has the ability to use the "exports" field of package.json 26 | to resolve non-relative imports. 27 | 28 | See the `/2-export-map` folder for an example, and its [readme](2-export-map/README.md) for details. 29 | -------------------------------------------------------------------------------- /moduleResolution/Node/3-package/README.md: -------------------------------------------------------------------------------- 1 | # Package module resolution 2 | 3 | TypeScript does its best to mimic the module resolution features of the runtime it is configured for. 4 | With a `moduleResolution` of `"Node"`, it will look for a file named "package.json", which in Node.js 5 | is used to define a package, and often specifies the main entrypoint in a field named `"main"`. 6 | A TypeScript main "entrypoint" (i.e. types for the main entrypoint) can be specified using a field named 7 | `"typings"` or `"types"` (they work the same). 8 | 9 | ## Examples 10 | 11 | These examples demonstrate TypeScript module resolution when using package.json files. 12 | The packages being imported are in `node_modules`, but they don't need to be for package.json 13 | processing to work. These could also be relative imports to a package inside your monorepo project, 14 | for instance. 15 | 16 | In these examples, the contents of `pkg/file.d.ts` differ from `pkg/file.ts`, 17 | to make it more clear which is being used in the emitted definition files. 18 | 19 | ### example1.ts 20 | 21 | This example imports from `"pkg"`, which specifies `"types": "./file.d.ts"` in its package.json file. 22 | 23 | Run `npm run build1` to view the module resolution trace and the resulting `example1.d.ts` file. 24 | 25 | ### example2.ts 26 | 27 | This example causes TypeScript to use a bit more complex logic to find type definitions. 28 | The example imports from `"pkg-only-main"`, which specifies only a `"main"` field in its package.json 29 | (no "types" field). TypeScript is able to use the "main" field to locate "file.ts", and uses 30 | its types. 31 | 32 | Run `npm run build2` to see the details of the resolution process that TypeScript uses. 33 | -------------------------------------------------------------------------------- /moduleResolution/Node/README.md: -------------------------------------------------------------------------------- 1 | # moduleResolution: "Node" 2 | 3 | Currently the most common `moduleResolution` setting for TypeScript is `"Node"`. 4 | It mimics many of the module resolution features of Node.js, specifically the 5 | CommonJS module resolution of Node.js version 11 and lower. 6 | 7 | This is also the recommended setting when using a bundler like Webpack or Vite. 8 | These have their own module resolution rules, but in general they more-or-less 9 | match up with the behavior of Node.js. However, new `moduleResolution` settings 10 | are currently being developed to allow more flexibility in the resolution features 11 | that TypeScript uses, in order to better support different bundlers and runtimes 12 | like browser ES Modules (ESM). 13 | 14 | Speaking of ESM, the `"Node"` setting does not support Node ESM or features like 15 | `"exports"` in package.json files. For this, `"Node16"` or `"NodeNext"` is required. 16 | Read more in the [README](../Node16/README.md) for the Node16 examples. 17 | 18 | ## Examples 19 | 20 | The examples in this directory all use `"moduleResolution": "Node"`, and are loosely 21 | ordered from simple to more complex. Each example contains a `README.md` file to explain 22 | the concepts demonstrated, one or more `example.ts` files performing an import, 23 | a `package.json` file defining one or more build scripts to generate type definition files 24 | and print module resolution debugging information, and one or more `tsconfig.json` files 25 | specifying any special settings for the example. 26 | 27 | The examples are: 28 | 29 | - [1-relative](1-relative/README.md): Standard, boring relative imports to another file in your project 30 | - [2-non-relative](2-non-relative/README.md): Imports starting without `"/"`, `"./"`, or `"../"` are pulled from node_modules 31 | - [3-package](3-package/README.md): TypeScript can use a package.json file to determine which module to use 32 | - [4-index](4-index/README.md): Node.js has special rules for files named `index.js`. TypeScript does too 33 | - [5-javascript](5-javascript/README.md): Can we import from a JavaScript file? If so, is there a way to still have type safety? 34 | - [6-mixed-ts-js](6-mixed-ts-js/README.md): What if we import from a JavaScript file, but there's also a TypeScript file with the same name? 35 | - [7-typesVersions](7-typesVersions/README.md): A way to publish multiple type definition files for different TypeScript versions 36 | -------------------------------------------------------------------------------- /moduleResolution/Node16/1-relative/README.md: -------------------------------------------------------------------------------- 1 | # Resolving modules with relative imports 2 | 3 | The rules for resolving module imports when using a `moduleResolution` of `"Node16"` or `"NodeNext"` are similar to the standard Node.js rules, but have some extra restrictions when using ES modules: 4 | 5 | 1. Extensionless specifiers are not allowed. 6 | You must provide the extension of the expected _compiled_ file, 7 | which normally means using an extension of `.js` or `.mjs`. 8 | TypeScript does not change import specifiers when it emits JavaScript, 9 | and so you need to use the final emitted filename in order for the generated code to be valid. 10 | 2. There's no special-handling for files named `index.js`. 11 | Since all relative imports require a full extension, there's no longer any way to specify a 12 | folder name and automatically resolve to an index file. 13 | 14 | ## Examples 15 | 16 | Note that the package.json in this folder does not specify `"type": "module"`, so files ending in `.ts` are considered to be CommonJS. 17 | 18 | ### example-cjs.ts 19 | 20 | This file will be treated as a CommonJS module, and is therefore able to use extensionless imports, which will resolve to the `target.ts` file (which is also CJS). 21 | 22 | Run `npm run build-cjs` to emit a type declaration file and view the module resolution trace. 23 | 24 | ### example-esm.mts 25 | 26 | Since this file uses a `.mts` extension, it will be treated as an ES module, which means that a full filename 27 | of the import specifier, including the extension, must be provided. 28 | 29 | Note that the extension is `.mjs` and not `.mts`, because we must provide the final _compiled_ file's path. 30 | TypeScript does not rewrite import specifiers, and so if we used `.mts`, the final compiled file would also include `.mts`, 31 | and Node.js would not know how to handle that TypeScript file at runtime. 32 | 33 | Run `npm run build-esm` to compile the types and view the module resolution trace. 34 | 35 | ### example-extensionless.mts 36 | 37 | The only real difference between this file and `example-cjs.ts` is that this file ends in `.mts`, 38 | so therefore TypeScript will treat it as an ES module, which means an extension is required in the import specifier. 39 | Without providing it, TypeScript will still create a type definition file, but the type inside will be `any`, 40 | and it will print a TypeScript error to the terminal. 41 | 42 | Run `npm run build-extensionless` to check it out. 43 | -------------------------------------------------------------------------------- /moduleResolution/Node16/2-export-map/README.md: -------------------------------------------------------------------------------- 1 | # Resolving modules with conditional exports 2 | 3 | Node.js introduced a new field called `"exports"` into package.json in version 12. 4 | It is a more powerful and flexible version of `"main"`, which allows package authors 5 | to specify the entrypoint(s) to their package. The `"exports"` field differs from `"main"` in a few ways: 6 | 7 | 1. It allows more than one entrypoint to be specified 8 | 2. Any file not included cannot be imported by consumers 9 | 3. Conditional exports can specify different files for various cases (e.g. "require" vs "import") 10 | 4. Subpath exports can simplify import specifiers (e.g. `import "pkg/foo";` instead of `import "pkg/dist/esm/foo";`) 11 | 12 | Note: not all bundlers and runtimes support all these features. See https://github.com/andrewbranch/example-subpath-exports-ts-compat for an explanation of the issues and a few compatibility strategies. 13 | 14 | ## TypeScript module resolution 15 | 16 | ### Prerequisites 17 | 18 | In order to use package.json `"exports"` fields in TypeScript, you must use `--moduleResolution Node16` (or `NodeNext`, they're the same for now). This is because the default `node` resolution mode is based on node.js 10, which does not include `"exports"` support. 19 | 20 | ### Example package with `"exports"` field 21 | 22 | Let's consider importing a value from a package which uses conditional exports to expose a different main entrypoint for consumers using either CommonJS (`"require"`) or ESM (`"import"`). An example can be found in this repo in `node_modules/pkg`. It uses `"type": "module"` in its package.json file, which means by default, `.js` (and `.ts`) files are considered to be ES modules. It also specifies an `"exports"` field: 23 | 24 | ```json 25 | "exports": { 26 | ".": { 27 | "import": { 28 | "types": "./index.d.ts", 29 | "default": "./index.js" 30 | }, 31 | "require": { 32 | "types": "./index.d.cts", 33 | "default": "./index.cjs" 34 | } 35 | }, 36 | "./package.json": "./package.json" 37 | } 38 | ``` 39 | 40 | There are a few things to note here: 41 | 42 | - The top-level entrypoint is specified by `"."` 43 | - Conditional imports are used for `"import"` and `"require"` 44 | - Separate types files are supplied to each conditional export, and these `"types"` fields are specified _before_ `"default"` 45 | - An additional entrypoint is given for `"./package.json"`. It is best practice to include this, otherwise consuming projects are not able to access this file. 46 | 47 | To make it clear which type file is being used, the example definitions are: 48 | 49 | ``` 50 | // pkg/index.d.ts 51 | export declare const value: "esm"; 52 | 53 | // pkg/index.d.cts 54 | export declare const value: "cjs"; 55 | ``` 56 | 57 | ### Module resolution 58 | 59 | If we want to import from this example project, and are using a `moduleResolution` setting 60 | of `"Node16"` or `"NodeNext"`, TypeScript will use the `"exports"` field of `pkg/package.json` to 61 | determine which types module to use. But whether it uses the `"import"` or `"require"` condition 62 | depends on a few things. 63 | 64 | 1. Is the import static or dynamic? 65 | 1. If the import is dynamic (`import('pkg')`), use the `"import"` condition. 66 | 2. If the import is static (`import { value } from 'pkg';`), use `"import"` or `"require"` depending on step 2: 67 | 2. Is the TypeScript file with the import an ES module? If so, use `"import"` condition, otherwise `"require"` 68 | 1. Files named `.mts` are always considered ES modules. 69 | 2. Files named `.cts` are always considered CommonJS modules. 70 | 3. Files named `.ts` are CommonJS by default, unless `"type": "module"` is set in package.json. 71 | 72 | ### Examples 73 | 74 | There are a few examples in this folder to demonstrate the various cases. 75 | 76 | #### /example1-static 77 | 78 | This project contains two files, `static.cts` and `static.mts`, with identical contents: 79 | 80 | ```ts 81 | import {value} from 'pkg'; 82 | 83 | export function Static() { 84 | return value; 85 | } 86 | ``` 87 | 88 | You can run `npm run build` in the folder to generate type definition files. A trace of 89 | TypeScript's module resolution will also print to the terminal, which can be helpful to 90 | understanding the process it went through to resolve the types. 91 | 92 | You can see that the extension of the files is used to determine which condition to use 93 | from `pkg`, and the generated types reflect the different conditions used. 94 | 95 | ```ts 96 | // static.d.cts 97 | export declare function Static(): "cjs"; 98 | 99 | // static.d.mts 100 | export declare function Static(): "esm"; 101 | ``` 102 | 103 | #### /example2-dynamic 104 | 105 | This example is similar to the static case, there are two files, `dynamic.cts` and `dynamic.mts`, 106 | and they contain the same code, this time with a dynamic import: 107 | 108 | ```ts 109 | export async function Dynamic() { 110 | return (await import("pkg")).value; 111 | } 112 | ``` 113 | 114 | This time, since typescript always treats dynamic imports as ESM, we get this result after running `npm run build`: 115 | 116 | ```ts 117 | // static.d.cts 118 | export declare function Dynamic(): Promise<"esm">; 119 | 120 | // static.d.mts 121 | export declare function Dynamic(): Promise<"esm">; 122 | ``` 123 | 124 | #### /example3-mixed 125 | 126 | This example demonstrates what happens if a `.cts` file uses a mixture of static and dynamic imports. 127 | In short, it's what we would expect. Dynamic imports use the `"import"` condition and static imports 128 | use `"require"`: 129 | 130 | ```ts 131 | // mixed.d.cts 132 | export declare function Static(): "cjs"; 133 | export declare function Dynamic(): Promise<"esm">; 134 | ``` 135 | 136 | ### Note about resolution-mode 137 | 138 | There are experimental ways to override which resolution mode is used, some of which is currently 139 | limited to TypeScript nightly versions. 140 | See https://devblogs.microsoft.com/typescript/announcing-typescript-4-7-rc/#resolution-mode for details. 141 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TypeScript Module Resolution Examples 2 | 3 | A set of example projects exploring TypeScript module resolution with various settings and situations. 4 | 5 | Table of contents: 6 | 7 | - [About this project](#about-this-project) 8 | - [Introduction to TS module resolution](#introduction-to-ts-module-resolution) 9 | - [Modules](#modules) 10 | - [Module resolution](#module-resolution) 11 | - [TypeScript Options](#typescript-options) 12 | - [`moduleResolution`](#moduleresolution) 13 | - [New options in development](#new-options-in-development) 14 | - [`baseUrl`](#baseurl) 15 | - [`moduleSuffixes`](#modulesuffixes) 16 | - [`resolution-mode`](#resolution-mode) 17 | - [Miscellaneous](#miscellaneous) 18 | - [Ambient modules](#ambient-modules) 19 | - [TS V5](#ts-v5) 20 | - [TS V5.0](#ts-v50) 21 | - [Resolution Customization Flags](#resolution-customization-flags) 22 | - [ModuleResolution Bundler](#moduleresolution-bundler) 23 | - [TS V5.1](#ts-v51) 24 | - [TypeRoots Are Consulted In Module Resolution](#typeroots-are-consulted-in-module-resolution) 25 | - [Explicit TypeRoots Disables Upward Walks for node_modules/@types](#explicit-typeroots-disables-upward-walks-for-node_modulestypes) 26 | - [TS V5.2](#ts-v52) 27 | - [Module and moduleResolution Must Match Node.js Settings](#module-and-moduleresolution-must-match-nodejs-settings) 28 | - [Type-Only Import Paths + TypeScript Implementation File Extensions](#type-only-import-paths--typescript-implementation-file-extensions) 29 | 30 | ## About this project 31 | 32 | This was initially created for [a talk](https://www.youtube.com/watch?v=MEl2R7mEAP8) at Michigan TypeScript Developers. 33 | 34 | The repository is roughly organized around TypeScript settings, with subfolders for possible values for those settings 35 | and examples demonstrating various aspects of module resolution. There are `README.md` files in most examples to 36 | explain the concept being demonstrated. Usually you can run `npm run build` in each example folder to generate 37 | `example.d.ts` type definition files and see a log of the module resolution process that TypeScript uses printed 38 | to the terminal. 39 | 40 | Feel free to submit issues or pull requests. I hope for this to be a learning resource for TypeScript developers who 41 | want to understand the complex topic of TypeScript module resolution. So with that, let's start learning! 42 | 43 | ## Introduction to TS module resolution 44 | 45 | ### Modules 46 | 47 | Let's begin by defining what we mean by a "module". When we write TypeScript code, 48 | we typically don't write everything in one single file. Instead, we break up our code 49 | into smaller pieces and put these into files. JavaScript can treat each file as either 50 | a "script" or a "module". 51 | 52 | Basically, a script file is a standard JavaScript file that does not import or require any other files 53 | and does not have any exports. It might operate on the global scope, or it might not. 54 | 55 | JavaScript Modules, on the other hand, generally come in two flavors, CommonJS (CJS) and 56 | ES Modules (ESM). Node.js historically used CJS and has started supporting ESM since version 12. 57 | Modern browsers also support ES modules, but they use slightly different rules for resolving 58 | imports from other modules. To learn more about ES modules in the browser, I recommend reading the 59 | [MDN article](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules). 60 | 61 | In TypeScript, most files you write will be considered a module. By default, any file that uses `import` or `export` is treated as a module. Starting in version 4.7, in some situations TypeScript will also check for a package.json file containing `"type": "module"`. The module detection behavior can be 62 | adjusted with the [`moduleDetection`](https://www.typescriptlang.org/tsconfig#moduleDetection) setting. 63 | 64 | ### Module resolution 65 | 66 | When we need to use code from another TypeScript file than the one we are writing, we generally 67 | use an `import` statement (or `import()` function). We might write something like: 68 | 69 | ```ts 70 | import foo from "moduleA"; 71 | ``` 72 | 73 | TypeScript will then need to find what module we mean by "moduleA", in order to determine what type 74 | "foo" should have. 75 | 76 | This is important. We are talking about _types_ when we talk about TypeScript module resolution. 77 | 78 | It's true that TypeScript can also compile `.ts` files into `.js` files, converting our TypeScript into 79 | JavaScript that can run in Node.js, or browsers, or elsewhere. But it's the runtime that will perform 80 | the actual imports, using its own module resolution algorithms. Module resolution rules are left up to the runtime to determine, it is not determined by the JavaScript specification. 81 | 82 | So, TypeScript does its best to try to match the same module resolution rules that the final runtime 83 | will use, so that the type information it gathers is complete and accurate. Because there are different 84 | runtimes with different rules, TypeScript exposes some options that we can set in order to match up with 85 | the runtime environment. 86 | 87 | ## TypeScript Options 88 | 89 | These are some options which TypeScript uses to adjust its module resolution. Please see the 90 | [TypeScript config reference](https://www.typescriptlang.org/tsconfig) for details on each option 91 | and the minimum TypeScript version required for support. 92 | 93 | Note that some module-related options, such as `module` and `target` effect the emitted JavaScript files, but not module resolution (other than by changing the defaults for other options in some cases), so they are not explored here. 94 | 95 | ### `moduleResolution` 96 | 97 | [Config reference](https://www.typescriptlang.org/tsconfig#moduleResolution) 98 | 99 | This is the most direct way to change TypeScript's module resolution mode. It has three current possible values, 100 | and more are being developed to better meet the needs of bundlers and browser-native modules. 101 | 102 | The currently-supported values are (as of TS 4.9): 103 | 104 | - `"Classic"` 105 | - `"Node"` 106 | - `"Node16"` or `"NodeNext"` (currently these mean the same thing) 107 | 108 | "Classic" mode is rarely used in modern projects, and is not explored in this repository. For details on how it works, 109 | see the TypeScript [module resolution guide](https://www.typescriptlang.org/docs/handbook/module-resolution.html#classic). 110 | 111 | "Node" is likely the most commonly-used option, and mimics the behavior of Node 11 and below. 112 | See the `moduleResolution/Node` examples and [README](moduleResolution/Node/README.md). 113 | 114 | "NodeNext" is intended to evolve as Node.js adds new versions and new features, but for now it is the same as "Node16", 115 | which adds support for the Node.js style of ES modules. There are some differences in TypeScript Module resolution 116 | compared to "Node", and these are explored in `moduleResolution/Node16`. 117 | See the [README](moduleResolution/Node16/README.md) there to begin. 118 | 119 | #### New options in development 120 | 121 | The TypeScript team identified that the current `moduleResolution` options are not flexible enough for those using 122 | bundlers or writing ESM to be directly consumed in browsers, so they are creating new options: 123 | 124 | - `"Hybrid"` (final naming TBD): designed primarily for bundlers, with some behavior customizable by settings. 125 | - PR: https://github.com/microsoft/TypeScript/pull/51669 126 | - `"Minimal"`: A common-denominator for resolution features supported in browsers, Node, Deno, and bundlers. 127 | - PR: https://github.com/microsoft/TypeScript/pull/50153 128 | 129 | There are no examples of these in this repo, but I intend to add them once they are released. 130 | 131 | ### `baseUrl` 132 | 133 | [Config reference](https://www.typescriptlang.org/tsconfig#baseUrl); 134 | 135 | _Examples TBD_ 136 | 137 | Provides a way to inform TypeScript that when compiled, non-relative imports can be resolved relative to this path. 138 | It is similar to the (discouraged) `NODE_PATH` option for Node.js. 139 | 140 | ### `moduleSuffixes` 141 | 142 | [Release announcement](https://devblogs.microsoft.com/typescript/announcing-typescript-4-7/#resolution-customization-with-modulesuffixes) 143 | 144 | _Examples TBD_ 145 | 146 | Allows customizing the “sub” extensions TS will examine, which can be particularly useful when targeting React Native 147 | as a runtime. For example, `"moduleSuffixes": [".ios", ".native", ""]` will look for files ending with `.ios.ts`, `.native.ts`, then `.ts`. 148 | 149 | ### `resolution-mode` 150 | 151 | [Release announcement](https://devblogs.microsoft.com/typescript/announcing-typescript-4-7/#resolution-mode) 152 | 153 | _Examples TBD_ 154 | 155 | This is an experimental feature, parts of which are available only in nightly versions, which can change the 156 | "resolution mode" of globals or imported types, allowing CJS types to be used in ESM files, and vice-versa. 157 | 158 | ## Miscellaneous 159 | 160 | ### Ambient modules 161 | 162 | Ambient modules make it possible to import a JavaScript project without types, and use 163 | [community-supplied](https://github.com/DefinitelyTyped/DefinitelyTyped) types or custom-written types to 164 | describe that project. The examples in `misc/ambient-modules` explore different ways that ambient modules can be 165 | configured and used. 166 | 167 | ## TS V5 168 | 169 | ### TS V5.0 170 | 171 | #### Resolution Customization Flags 172 | 173 | - [Youtube - Resolution Customization Flags](https://youtu.be/uKcfxI84xPc?si=4C8Fa1kGB-roejUP) 174 | 175 | #### ModuleResolution Bundler 176 | 177 | - [Youtube - --moduleResolution bundler](https://youtu.be/wfCRQF-HxwY?si=E28B1MipgyElA8QS) 178 | 179 | ### TS V5.1 180 | 181 | #### TypeRoots Are Consulted In Module Resolution 182 | 183 | - [Youtube - typeRoots Are Consulted In Module Resolution](https://youtu.be/Z5Qk6z9RK4s?si=-mPoX2bmxp0ey5oK) 184 | 185 | #### Explicit TypeRoots Disables Upward Walks for node_modules/@types 186 | 187 | - [Youtube - Explicit typeRoots Disables Upward Walks for node_modules/@types](https://youtu.be/avVimJ1gdNA?si=zm-lwIQHCNo3JA-j) 188 | 189 | ### TS V5.2 190 | 191 | #### Module and moduleResolution Must Match Node.js Settings 192 | 193 | - [Youtube - module and moduleResolution Must Match Node.js Settings](https://youtu.be/j3OUn0iw4k8?si=Km4-xA1FkmpW6gaj) 194 | 195 | #### Type-Only Import Paths + TypeScript Implementation File Extensions 196 | 197 | - [Youtube - Type-Only Import Paths + TypeScript Implementation File Extensions](https://youtu.be/ZwNIsSmk-Ks?si=XGQl-BmzXYOGUhYF) 198 | --------------------------------------------------------------------------------