UNPKG

@septh/ts-run

Version:

The minimalist TypeScript script runner for Node

93 lines (80 loc) 2.97 kB
import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { readFile } from 'node:fs/promises'; import { transform } from './transform.cjs'; const jsExtRx = /\.([cm])?js$/; const tsExtRx = /\.([cm])?ts$/; const hookData = Object.create(null); const initialize = ({ self, defaultModuleType }) => { hookData.self = self; hookData.defaultModuleType = defaultModuleType; }; const resolve = async (specifier, context, nextResolve) => { // Try first with the .ts extension, otherwise go as-is. try { return await nextResolve(specifier.replace(jsExtRx, '.$1ts'), context) } catch { return nextResolve(specifier, context) } }; const pkgTypeCache = new Map(); async function nearestPackageType(file) { for ( let current = path.dirname(file), previous = undefined; previous !== current; previous = current, current = path.dirname(current) ) { const pkgFile = path.join(current, 'package.json'); let cached = pkgTypeCache.get(pkgFile); if (cached === undefined) { cached = await readFile(pkgFile) .then(data => { const { type } = JSON.parse(data.toString()); return type === 'module' || type === 'commonjs' ? type : hookData.defaultModuleType }) .catch(err => { const { code } = err; if (code !== 'ENOENT') console.error(err); return null }); pkgTypeCache.set(pkgFile, cached); } if (typeof cached === 'string') return cached } return hookData.defaultModuleType } const load = async (url, context, nextLoad) => { // If this is not a TypeScript file, defer to the next hook in the chain. const fileUrl = new URL(url); const { protocol, pathname } = fileUrl; const [ ext ] = tsExtRx.exec(pathname) ?? []; if (protocol !== 'file:' || !ext) return nextLoad(url, context) // Determine the format based on the file's extension // or the nearest package.json's `type` field. const filePath = fileURLToPath(fileUrl); const format = (context.format ?? ( ext === '.ts' ? await nearestPackageType(filePath) : ext === '.mts' ? 'module' : 'commonjs' )).replace(/-typescript$/, ''); if (format !== 'module' && format !== 'commonjs') return nextLoad(url, context) // Load and transform the file. const source = await readFile(filePath).then( buffer => transform(buffer.toString(), format, path.basename(filePath)) ); return { source, format, shortCircuit: true, } }; export { initialize, load, resolve };