TypeScript and JSX

Run TypeScript, JSX/TSX, and ESM or CommonJS test files with source maps.

#

Guchho Test supports JavaScript, TypeScript, and JSX/TSX test files.

TypeScript and JSX are transformed on load, so test files do not need a separate build step. Inline source maps keep stack traces, coverage, and snapshots tied to the original source.

Supported extensions

#
Extension Module format Transformation
.js ESM none
.mjs ESM none
.cjs CommonJS none
.ts ESM TypeScript → ESM
.mts ESM TypeScript → ESM
.cts CommonJS TypeScript → CommonJS
.jsx ESM JSX → React.createElement
.tsx ESM TypeScript + JSX → ESM

How transformation works

#

When the CLI starts, it registers a Node.js loader hook. Transformable files are read, transpiled with the TypeScript compiler, and supplied to Node.js with an inline source map.

Configuration:

  • target: ES2022;
  • module: ESNext for ESM files, CommonJS for .cts;
  • JSX: classic runtime (React.createElement);
  • isolatedModules: true;
  • source maps with inline sources.

Transformation diagnostics are reported as syntax errors with file, line, and column.

Path aliases

#

Imports that match compilerOptions.paths are resolved before Node.js sees them. The CLI auto-detects tsconfig.json in the working directory, or you can select a file explicitly:

export default defineConfig({
  tsconfig: './tsconfig.build.json',
});

Given:

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@app/*": ["src/*"]
    }
  }
}

both of these resolve to files under src/:

import { createUser } from '@app/users';
import { createUser } from '@app/users.js';

Explicit .js, .jsx, .mjs, and .cjs specifiers also try their TypeScript equivalents (.ts, .tsx, .mts, .cts), matching Node.js TypeScript resolution. Extensionless specifiers try every supported extension and index files. Relative imports, absolute paths, node: built-ins, file: URLs, and other URL schemes are not rewritten.

Type checking

#

Transformation does not type-check. Run type checking separately, for example:

npx tsc --noEmit

This project uses a strict tsconfig.json and a tsconfig.build.json for declarations.

Source maps

#

Inline source maps let stack traces and coverage point at original source lines, including for TypeScript and TSX files. Source-map remapping is used by the coverage pipeline.

JSX

#

JSX uses the classic runtime. A React.createElement implementation must be in scope, or you can provide a JSX factory through your own setup. Example with a minimal factory:

function createElement(type, props, ...children) {
  return { type, props, children };
}

it('renders', () => {
  const element = <div>Hello</div>;
  expect(element.type).toBe('div');
});

Snapshots in TypeScript

#

Inline snapshot updates locate the call in the original source using the TypeScript parser and replace the argument in place. This works for .ts and .tsx files.

CommonJS

#

.cts files are transformed to CommonJS. Other transformed files are ESM. Note that module mocking (mock.module) targets ESM imports.

Declaration files

#

The build emits TypeScript declarations (.d.ts) alongside compiled JavaScript under dist/. Consumers get types through the package export map:

{
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "default": "./dist/index.js"
    },
    "./config": {
      "types": "./dist/config/index.d.ts",
      "default": "./dist/config/index.js"
    }
  }
}

Best practices

#
  • Use the same module format and path aliases as the application.
  • Keep type-only imports explicit so transformation stays predictable.
  • Run tsc --noEmit separately when full type checking is required.
  • Prefer .ts or .tsx test files in TypeScript projects.

Requirements

#
  • Node.js >=20.19.0 (required for stable loader behavior used by the transformer and module mocks).
  • TypeScript ^5.7.0 is used by the transformer.
#

Edit this page on GitHub