Registry
J

correct-ts-specifiers

No description available

jakobjingleheimer1 executions

  • Legacy
  • esm
  • import
  • node
  • typescript
Connect a repo
npx codemod correct-ts-specifiers

Correct TypeScript Specifiers

This package transforms import specifiers from the old tsc (TypeScript's compiler) requirement of using .js file extensions in source-code to import files that are actually typescript; the corrected specifiers enable source-code to be runnable by standards-compliant software like Node.js.

This is a one-and-done process, and the updated source-code should be committed to your version control (eg git); thereafter, source-code import statements should be authored compliant with the ECMAScript (JavaScript) standard.

This package does not just blindly find & replace file extensions within specifiers: It confirms that the replacement specifier actually exists; in ambiguous cases (such as two files with the same basename in the same location but different relevant file extensions like /tmp/foo.js and /tmp/foo.ts), it logs an error, skips that specifier, and continues processing.

Running

sh
NODE_OPTIONS="--experimental-import-meta-resolve" \
npx codemod@latest correct-ts-specifiers

Monorepos

For best results, run this within each workspace of the monorepo.

text
project-root/
  ├ workspaces/
    ├ foo/ ←--------- RUN HERE
      ├ …
      ├ package.json
      └ tsconfig.json
    └ bar/ ←--------- RUN HERE
      ├ …
      ├ package.json
      └ tsconfig.json
  └ utils/ ←--------- RUN HERE
    ├ qux.js
    └ zed.js

Supported cases

  • no file extension → .cts, .mts, .js, .ts, .d.cts, .d.mts, or .d.ts
  • .cjs → .cts, .mjs → .mts, .js → .ts
  • .js → .d.cts, .d.mts, or .d.ts
  • Package.json subimports
  • tsconfig paths (via @nodejs-loaders/alias)
    • In order to subsequently run code via node, you will need to add this (or another) loader to your own project. Or, switch to subimports.
  • Commonjs-like directory specifiers

Before:

ts
import { URL } from 'node:url';

import { bar } from '@dep/bar';
import { foo } from 'foo';

import { Bird } from './Bird';          // a directory
import { Cat } from './Cat.ts';
import { Dog } from '…/Dog/index.mjs';  // tsconfig paths
import { baseUrl } from '#config.js';   // package.json imports

export { Zed } from './zed';

export const makeLink = (path: URL) => (new URL(path, baseUrl)).href;

const nil = await import('./nil.js');

const bird = new Bird('Tweety');
const cat = new Cat('Milo');
const dog = new Dog('Otis');

After:

ts
import { URL } from 'node:url';

import { bar } from '@dep/bar';
import { foo } from 'foo';

import { Bird } from './Bird/index.ts';
import { Cat } from './Cat.ts';
import { Dog } from '…/Dog/index.mts';  // tsconfig paths
import { baseUrl } from '#config.js';   // package.json imports

export type { Zed } from './zed.d.ts';

export const makeLink = (path: URL) => (new URL(path, baseUrl)).href;

const nil = await import('./nil.ts');

const bird = new Bird('Tweety');
const cat = new Cat('Milo');
const dog = new Dog('Otis');

Ready to evolve your codebase?

Build tailored, compiler-aware skills to assess, plan, and automate complex codebase changes.