Utilities for working with tsconfig
files
- What is this?
- When should I use this?
- Install
- Use
- API
COMPILER_OPTIONS
LIB
loadCompilerOptions(tsconfig[, options])
loadLib(tsconfig[, options])
loadPaths(tsconfig[, options])
loadPlugins(tsconfig[, options])
loadTsconfig(id[, options])
normalizeCompilerOptions(option)
normalizeImportsNotUsed(option)
normalizeJsx(option)
normalizeLib(option)
normalizeModule(option)
normalizeModuleDetection(option)
normalizeModuleResolution(option)
normalizeNewLine(option)
normalizeTarget(option)
resolvePaths(code, options)
- Types
- Related
- Contribute
This package exports a set of utilities for working with TypeScript configuration files.
This package can be used to load tsconfig files, resolve path aliases, and normalize user options for the TypeScript Compiler API.
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.
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 😊
This package exports the following identifiers:
COMPILER_OPTIONS
LIB
loadCompilerOptions
loadLib
loadPaths
loadPlugins
loadTsconfig
normalizeCompilerOptions
normalizeImportsNotUsed
normalizeJsx
normalizeLib
normalizeModule
normalizeModuleDetection
normalizeModuleResolution
normalizeNewLine
normalizeTarget
resolvePaths
There is no default export.
Set containing compiler option names.
Map containing type definition library names that correspond to files in the **/node_modules/typescript/lib
directory.
All keys are lowercase.
Loads compilerOptions
from a tsconfig file.
{mlly.ModuleId}
tsconfig
— Module id of tsconfig file{LoadTsconfigOptions?}
[options]
— Tsconfig loading options
{CompilerOptions}
Compiler options object.
Loads type definition library names from a tsconfig file.
{mlly.ModuleId}
tsconfig
— Module id of tsconfig file{LoadTsconfigOptions?}
[options]
— Tsconfig loading options
{Lib[]}
Type definition library names array.
Loads a path alias configuration from a tsconfig file.
{mlly.ModuleId}
tsconfig
— Module id of tsconfig file{LoadTsconfigOptions?}
[options]
— Tsconfig loading options
{Paths}
Path alias configuration object.
Loads language service plugin configurations from a tsconfig file.
{mlly.ModuleId}
tsconfig
— Module id of tsconfig file{LoadTsconfigOptions?}
[options]
— Tsconfig loading options
{Plugin[]}
Language service plugin configurations array.
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.
{mlly.ModuleId}
id
— Module id of tsconfig file{LoadTsconfigOptions?}
[options]
— Load options
{Nullable<TSConfig>}
User configuration object or null
if tsconfig file is not found.
Converts the given compilerOptions
into programmatic compiler options.
TypeScript programs expect compiler option objects to use enum values where appropriate.
{unknown}
compilerOptions
— User compiler options
{ts.CompilerOptions}
Programmatic compiler options.
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.
{unknown}
option
— Option to evaluate
{ts.ImportsNotUsedAsValues | undefined}
ts.ImportsNotUsedAsValues
value or undefined
.
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.
{unknown}
option
— Option to evaluate
{ts.JsxEmit | undefined}
ts.JsxEmit
value or undefined
.
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.
{unknown}
option
— Option to evaluate
{LibFile[]}
Lib filename array.
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.
{unknown}
option
— Option to evaluate
{ts.ModuleKind | undefined}
ts.ModuleKind
value or undefined
.
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.
{unknown}
option
— Option to evaluate
{ts.ModuleDetectionKind | undefined}
ts.ModuleDetectionKind
value or undefined
.
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.
{unknown}
option
— Option to evaluate
{ts.ModuleResolutionKind | undefined}
ts.ModuleResolutionKind
value or undefined
.
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.
{unknown}
option
— Option to evaluate
{ts.NewLineKind | undefined}
ts.NewLineKind
value or undefined
.
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.
{unknown}
option
— Option to evaluate
{ts.ScriptTarget | undefined}
ts.ScriptTarget
value or undefined
.
Resolves path aliases in export
, import
, and require
statements in the given piece of source code
.
{string}
code
— Code to evaluate{ResolvePathsOptions}
options
— Path alias resolution options
{Promise<string>}
code
with path aliases resolved and/or unmodified.
This package is fully typed with TypeScript.
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.
mlly
— ECMAScript module utilitiestsconfig-types
— TypeScript definitions fortsconfig.json
See CONTRIBUTING.md
.