From 0b15536b1b021eec182b18e99d5ab2b6c9deacd3 Mon Sep 17 00:00:00 2001 From: Mike Donnalley Date: Fri, 3 Nov 2023 12:41:15 -0600 Subject: [PATCH] fix: handle non-existent tsconfig --- src/config/ts-node.ts | 11 +++++++---- src/util/fs.ts | 12 ++++++++++++ test/config/ts-node.test.ts | 18 +++++++++--------- 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/src/config/ts-node.ts b/src/config/ts-node.ts index eded54e39..6affb0d06 100644 --- a/src/config/ts-node.ts +++ b/src/config/ts-node.ts @@ -1,11 +1,10 @@ -import {readFile} from 'node:fs/promises' import {join, relative as pathRelative, sep} from 'node:path' import * as TSNode from 'ts-node' import {memoizedWarn} from '../errors' import {Plugin, TSConfig} from '../interfaces' import {settings} from '../settings' -import {existsSync} from '../util/fs' +import {existsSync, safeReadFile} from '../util/fs' import {isProd} from '../util/util' import Cache from './cache' import {Debug} from './util' @@ -38,11 +37,15 @@ function importTypescript(root: string) { async function loadTSConfig(root: string): Promise { try { if (TS_CONFIGS[root]) return TS_CONFIGS[root] - const tsconfigPath = join(root, 'tsconfig.json') + const typescript = importTypescript(root) if (!typescript) return - const {config} = typescript.parseConfigFileTextToJson(tsconfigPath, await readFile(tsconfigPath, 'utf8')) + const tsconfigPath = join(root, 'tsconfig.json') + const raw = await safeReadFile(tsconfigPath) + if (!raw) return + + const {config} = typescript.parseConfigFileTextToJson(tsconfigPath, raw) if (!config || Object.keys(config.compilerOptions).length === 0) return diff --git a/src/util/fs.ts b/src/util/fs.ts index 50e04a99f..667eb6753 100644 --- a/src/util/fs.ts +++ b/src/util/fs.ts @@ -60,12 +60,24 @@ export function readJsonSync(path: string, parse = true): T | strin return parse ? (JSON.parse(contents) as T) : contents } +/** + * Read a JSON file, returning undefined if the file does not exist. + */ export async function safeReadJson(path: string): Promise { try { return await readJson(path) } catch {} } +/** + * Read a file, returning undefined if the file does not exist. + */ +export async function safeReadFile(path: string): Promise { + try { + return await readFile(path, 'utf8') + } catch {} +} + export function existsSync(path: string): boolean { return fsExistsSync(path) } diff --git a/test/config/ts-node.test.ts b/test/config/ts-node.test.ts index 4ef4fdc19..6ccbe50ff 100644 --- a/test/config/ts-node.test.ts +++ b/test/config/ts-node.test.ts @@ -1,11 +1,11 @@ import {expect} from 'chai' -import fs from 'node:fs/promises' import {join, resolve} from 'node:path' import {SinonSandbox, createSandbox} from 'sinon' import * as tsNode from 'ts-node' import * as configTsNode from '../../src/config/ts-node' import {Interfaces, settings} from '../../src/index' +import * as util from '../../src/util/fs' const root = resolve(__dirname, 'fixtures/typescript') const tsSource = 'src/hooks/init.ts' @@ -42,25 +42,25 @@ describe('tsPath', () => { }) it('should resolve a .js file to ts src', async () => { - sandbox.stub(fs, 'readFile').resolves(JSON.stringify(DEFAULT_TS_CONFIG)) + sandbox.stub(util, 'safeReadFile').resolves(JSON.stringify(DEFAULT_TS_CONFIG)) const result = await configTsNode.tsPath(root, jsCompiled) expect(result).to.equal(join(root, tsModule)) }) it('should resolve a module file to ts src', async () => { - sandbox.stub(fs, 'readFile').resolves(JSON.stringify(DEFAULT_TS_CONFIG)) + sandbox.stub(util, 'safeReadFile').resolves(JSON.stringify(DEFAULT_TS_CONFIG)) const result = await configTsNode.tsPath(root, jsCompiledModule) expect(result).to.equal(join(root, tsModule)) }) it('should resolve a .ts file', async () => { - sandbox.stub(fs, 'readFile').resolves(JSON.stringify(DEFAULT_TS_CONFIG)) + sandbox.stub(util, 'safeReadFile').resolves(JSON.stringify(DEFAULT_TS_CONFIG)) const result = await configTsNode.tsPath(root, tsSource) expect(result).to.equal(join(root, tsSource)) }) it('should resolve a .ts file using baseUrl', async () => { - sandbox.stub(fs, 'readFile').resolves( + sandbox.stub(util, 'safeReadFile').resolves( JSON.stringify({ compilerOptions: { baseUrl: '.src/', @@ -73,19 +73,19 @@ describe('tsPath', () => { }) it('should resolve .ts with no outDir', async () => { - sandbox.stub(fs, 'readFile').resolves(JSON.stringify({compilerOptions: {rootDir: 'src'}})) + sandbox.stub(util, 'safeReadFile').resolves(JSON.stringify({compilerOptions: {rootDir: 'src'}})) const result = await configTsNode.tsPath(root, tsSource) expect(result).to.equal(join(root, tsSource)) }) it('should resolve .js with no rootDir and outDir', async () => { - sandbox.stub(fs, 'readFile').resolves(JSON.stringify({compilerOptions: {strict: true}})) + sandbox.stub(util, 'safeReadFile').resolves(JSON.stringify({compilerOptions: {strict: true}})) const result = await configTsNode.tsPath(root, jsCompiled) expect(result).to.equal(join(root, jsCompiled)) }) it('should resolve to .ts file if enabled and prod', async () => { - sandbox.stub(fs, 'readFile').resolves(JSON.stringify(DEFAULT_TS_CONFIG)) + sandbox.stub(util, 'safeReadFile').resolves(JSON.stringify(DEFAULT_TS_CONFIG)) settings.tsnodeEnabled = true const originalNodeEnv = process.env.NODE_ENV delete process.env.NODE_ENV @@ -98,7 +98,7 @@ describe('tsPath', () => { }) it('should resolve to js if disabled', async () => { - sandbox.stub(fs, 'readFile').resolves(JSON.stringify(DEFAULT_TS_CONFIG)) + sandbox.stub(util, 'safeReadFile').resolves(JSON.stringify(DEFAULT_TS_CONFIG)) settings.tsnodeEnabled = false const result = await configTsNode.tsPath(root, jsCompiled) expect(result).to.equal(join(root, jsCompiled))