@flex-development/tsconfig-utils
TypeScript icon, indicating that this package has built-in type declarations

2.0.2 • Public • Published

tsconfig-utils

github release npm codecov module type: esm license conventional commits typescript vitest yarn

Utilities for working with tsconfig files

Contents

What is this?

This package exports a set of utilities for working with TypeScript configuration files.

When should I use this?

This package can be used to load tsconfig files, resolve path aliases, and normalize user options for the TypeScript Compiler API.

Install

This package is ESM only.

yarn add @flex-development/tsconfig-utils

From Git:

yarn add @flex-development/tsconfig-utils@flex-development/tsconfig-utils
See Git - Protocols | Yarn  for details on requesting a specific branch, commit, or tag.

Use

Let's say a developer wants to run a TypeScript file using node. The developer has a tsconfig.json with path alias configurations.

They implement loader.mjs (and for good measure, typings/node/loader.d.ts too 😉):

/**
 * @file Custom Loader Hooks
 * @module loader
 * @see https://nodejs.org/api/esm.html#loaders
 */

import * as mlly from '@flex-development/mlly'
import * as pathe from '@flex-development/pathe'
import * as tscu from '@flex-development/tsconfig-utils'
import * as tutils from '@flex-development/tutils'
import * as esbuild from 'esbuild'
import { fileURLToPath, pathToFileURL } from 'node:url'

// add support for extensionless files in "bin" scripts
// https://github.com/nodejs/modules/issues/488
mlly.EXTENSION_FORMAT_MAP.set('', mlly.Format.COMMONJS)

/**
 * URL of tsconfig file.
 *
 * @type {import('node:url').URL}
 * @const tsconfig
 */
const tsconfig = mlly.toURL('tsconfig.json')

/**
 * TypeScript compiler options.
 *
 * @type {tscu.CompilerOptions}
 * @const compilerOptions
 */
const compilerOptions = tscu.loadCompilerOptions(tsconfig)

/**
 * Determines how the module at the given `url` should be interpreted,
 * retrieved, and parsed.
 *
 * @see {@linkcode LoadHookContext}
 * @see https://nodejs.org/api/esm.html#loadurl-context-nextload
 *
 * @async
 *
 * @param {string} url - Resolved module URL
 * @param {LoadHookContext} context - Hook context
 * @return {Promise<LoadHookResult>} Hook result
 */
export const load = async (url, context) => {
  // get module format
  context.format = context.format ?? (await mlly.getFormat(url))

  // validate import assertions
  mlly.validateAssertions(url, context.format, context.importAssertions)

  /**
   * File extension of {@linkcode url}.
   *
   * @type {pathe.Ext | tutils.EmptyString}
   * @const ext
   */
  const ext = pathe.extname(url)

  /**
   * Source code.
   *
   * @type {tutils.Optional<esm.Source<Uint8Array | string>>}
   * @var source
   */
  let source = await mlly.getSource(url, { format: context.format })

  // transform typescript files
  if (/^\.(?:cts|mts|tsx?)$/.test(ext) && !/\.d\.(?:cts|mts|ts)$/.test(url)) {
    // resolve path aliases
    source = await tscu.resolvePaths(source, {
      conditions: context.conditions,
      ext: '',
      parent: url,
      tsconfig
    })

    // resolve modules
    source = await mlly.resolveModules(source, {
      conditions: context.conditions,
      parent: url
    })

    // transpile source code
    const { code } = await esbuild.transform(source, {
      format: ext === '.cts' ? 'cjs' : 'esm',
      loader: ext.slice(/^\.[cm]/.test(ext) ? 2 : 1),
      minify: false,
      sourcefile: fileURLToPath(url),
      sourcemap: 'inline',
      target: `node${process.versions.node}`,
      tsconfigRaw: { compilerOptions }
    })

    // set source code to transpiled source
    source = code
  }

  return { format: context.format, shortCircuit: true, source }
}

/**
 * Resolves the given module `specifier`.
 *
 * Adds supports for:
 *
 * - Path alias resolution
 * - Extensionless file and directory index resolution
 *
 * @see {@linkcode ResolveHookContext}
 * @see https://nodejs.org/api/esm.html#resolvespecifier-context-nextresolve
 *
 * @async
 *
 * @param {string} specifier - Module specifier
 * @param {ResolveHookContext} context - Hook context
 * @return {Promise<ResolveHookResult>} Hook result
 * @throws {Error}
 */
export const resolve = async (specifier, context) => {
  const { conditions, parentURL: parent } = context

  // resolve path alias
  specifier = await mlly.resolveAlias(specifier, {
    aliases: tscu.loadPaths(tsconfig),
    conditions,
    cwd: pathToFileURL(compilerOptions.baseUrl),
    parent
  })

  /**
   * Resolved module URL.
   *
   * @type {import('node:url').URL}
   * @const url
   */
  const url = await mlly.resolveModule(specifier, {
    conditions,
    parent: parent?.startsWith('file:') ? parent : specifier
  })

  return {
    format: await mlly.getFormat(url),
    shortCircuit: true,
    url: url.href
  }
}

The developer creates scratch.ts to test their custom loader hooks:

/**
 * @file Scratch
 * @module scratch
 */

import { resolvePaths } from '@flex-development/tsconfig-utils'
import { dedent } from 'ts-dedent'

const code = dedent`
  import type { ResolveAliasOptions } from '#src/interfaces'
  import * as internal from '#src/internal'
  import loadCompilerOptions from '#src/utils/load-compiler-options'
  import * as mlly from '@flex-development/mlly'
`

console.debug(await resolvePaths(code, { ext: '', parent: import.meta.url }))

Running the file with node --loader=./loader.mjs ./scratch yields:

import type { ResolveAliasOptions } from './src/interfaces'
import * as internal from './src/internal'
import loadCompilerOptions from './src/utils/load-compiler-options'
import * as mlly from '@flex-development/mlly'

Pleased with their work, they crack open a cold Red Bull 😊

API

This package exports the following identifiers:

There is no default export.

COMPILER_OPTIONS

Set containing compiler option names.

Source

src/utils/compiler-options.ts

LIB

Map containing type definition library names that correspond to files in the **/node_modules/typescript/lib directory.

All keys are lowercase.

Source

src/utils/lib.ts

loadCompilerOptions(tsconfig[, options])

Loads compilerOptions from a tsconfig file.

Parameters

  • {mlly.ModuleId} tsconfig — Module id of tsconfig file
  • {LoadTsconfigOptions?} [options] — Tsconfig loading options

Returns

{CompilerOptions} Compiler options object.

Source

src/utils/load-compiler-options.ts

loadLib(tsconfig[, options])

Loads type definition library names from a tsconfig file.

Parameters

  • {mlly.ModuleId} tsconfig — Module id of tsconfig file
  • {LoadTsconfigOptions?} [options] — Tsconfig loading options

Returns

{Lib[]} Type definition library names array.

Source

src/utils/load-lib.ts

loadPaths(tsconfig[, options])

Loads a path alias configuration from a tsconfig file.

Parameters

  • {mlly.ModuleId} tsconfig — Module id of tsconfig file
  • {LoadTsconfigOptions?} [options] — Tsconfig loading options

Returns

{Paths} Path alias configuration object.

Source

src/utils/load-paths.ts

loadPlugins(tsconfig[, options])

Loads language service plugin configurations from a tsconfig file.

Parameters

  • {mlly.ModuleId} tsconfig — Module id of tsconfig file
  • {LoadTsconfigOptions?} [options] — Tsconfig loading options

Returns

{Plugin[]} Language service plugin configurations array.

Source

src/utils/load-plugins.ts

loadTsconfig(id[, options])

Reads and parses the tsconfig file at the given module id.

If the tsconfig file is found, comments and byte order marks (BOMs) will be removed before parsing. If successfully parsed, an object representation of the tsconfig file will be returned.

Extending configuration files is also supported. If not overwritten, the baseUrl, outDir, and rootDir properties from the base tsconfig file will be made relative to the tsconfig file being loaded.

Parameters

  • {mlly.ModuleId} id — Module id of tsconfig file
  • {LoadTsconfigOptions?} [options] — Load options

Returns

{Nullable<TSConfig>} User configuration object or null if tsconfig file is not found.

Source

src/utils/load-tsconfig.ts

normalizeCompilerOptions(compilerOptions)

Converts the given compilerOptions into programmatic compiler options.

TypeScript programs expect compiler option objects to use enum values where appropriate.

Parameters

  • {unknown} compilerOptions — User compiler options

Returns

{ts.CompilerOptions} Programmatic compiler options.

Source

src/utils/normalize-compiler-options.ts

normalizeImportsNotUsed(option)

Converts the given option into a programmatic importsNotUsedAsValues option.

TypeScript programs expect a ts.ImportsNotUsedAsValues value.

If the option is already programmatic, it will be returned unmodified. If it cannot be converted, undefined will be returned instead.

Parameters

  • {unknown} option — Option to evaluate

Returns

{ts.ImportsNotUsedAsValues | undefined} ts.ImportsNotUsedAsValues value or undefined.

Source

src/utils/normalize-imports-not-used.ts

normalizeJsx(option)

Converts the given option into a programmatic jsx option.

TypeScript programs expect a ts.JsxEmit value.

If the option is already programmatic, it will be returned unmodified. If it cannot be converted, undefined will be returned instead.

Parameters

  • {unknown} option — Option to evaluate

Returns

{ts.JsxEmit | undefined} ts.JsxEmit value or undefined.

Source

src/utils/normalize-jsx.ts

normalizeLib(option)

Converts the given option into an array containing programmatic lib options.

TypeScript programs expect values in compilerOptions.lib to match filenames in **/node_modules/typescript/lib exactly.

Parameters

  • {unknown} option — Option to evaluate

Returns

{LibFile[]} Lib filename array.

Source

src/utils/normalize-lib.ts

normalizeModule(option)

Converts the given option into a programmatic module option.

TypeScript programs expect a ts.ModuleKind value.

If the option is already programmatic, it will be returned unmodified. If it cannot be converted, undefined will be returned instead.

Parameters

  • {unknown} option — Option to evaluate

Returns

{ts.ModuleKind | undefined} ts.ModuleKind value or undefined.

Source

src/utils/normalize-module.ts

normalizeModuleDetection(option)

Converts the given option into a programmatic moduleDetection option.

TypeScript programs expect a ts.ModuleDetectionKind value.

If the option is already programmatic, it will be returned unmodified. If it cannot be converted, undefined will be returned instead.

Parameters

  • {unknown} option — Option to evaluate

Returns

{ts.ModuleDetectionKind | undefined} ts.ModuleDetectionKind value or undefined.

Source

src/utils/normalize-module-detection.ts

normalizeModuleResolution(option)

Converts the given option into a programmatic moduleResolution option.

TypeScript programs expect a ts.ModuleResolutionKind value.

If the option is already programmatic, it will be returned unmodified. If it cannot be converted, undefined will be returned instead.

Parameters

  • {unknown} option — Option to evaluate

Returns

{ts.ModuleResolutionKind | undefined} ts.ModuleResolutionKind value or undefined.

Source

src/utils/normalize-module-resolution.ts

normalizeNewLine(option)

Converts the given option into a programmatic newLine option.

TypeScript programs expect a ts.NewLineKind value.

If the option is already programmatic, it will be returned unmodified. If it cannot be converted, undefined will be returned instead.

Parameters

  • {unknown} option — Option to evaluate

Returns

{ts.NewLineKind | undefined} ts.NewLineKind value or undefined.

Source

src/utils/normalize-new-line.ts

normalizeTarget(option)

Converts the given option into a programmatic target option.

TypeScript programs expect a ts.ScriptTarget value.

If the option is already programmatic, it will be returned unmodified. If it cannot be converted, undefined will be returned instead.

Parameters

  • {unknown} option — Option to evaluate

Returns

{ts.ScriptTarget | undefined} ts.ScriptTarget value or undefined.

Source

src/utils/normalize-target.ts

resolvePaths(code, options)

Resolves path aliases in export, import, and require statements in the given piece of source code.

Parameters

  • {string} code — Code to evaluate
  • {ResolvePathsOptions} options — Path alias resolution options

Returns

{Promise<string>} code with path aliases resolved and/or unmodified.

Source

src/utils/resolve-paths.ts

Types

This package is fully typed with TypeScript.

Interfaces

tsconfig-types

This package re-exports TypeScript definitions from tsconfig-types. This is primarily for the convenience of TypeScript users who do not hoist packages, but may need to import definitions used in this package.

Related

Contribute

See CONTRIBUTING.md.

Package Sidebar

Install

npm i @flex-development/tsconfig-utils

Weekly Downloads

2

Version

2.0.2

License

BSD-3-Clause

Unpacked Size

182 kB

Total Files

83

Last publish

Collaborators

  • unicornware