From 188f13bcb8c7b5aa0617b899a892e41e33d63271 Mon Sep 17 00:00:00 2001 From: Alvis HT Tang Date: Sat, 30 Nov 2024 10:37:09 +0000 Subject: [PATCH] fix: make render function standalone BREAKING CHANGE: * the render method in the Xception instance has been removed * use renderError instead EXAMPLE MIGRATION before: ```ts const error = new Xception(...) const rendered = error.render(); ``` after: ``` import { renderError } from 'xception/render'; const error = new Xception(...) const rendered = renderError(error.render); ``` NOTE: separating the render function from the default export enables the use of other features in non-Node environments --- package.json | 4 ++++ source/base.ts | 12 ---------- source/import.ts | 16 ------------- source/index.ts | 1 - source/render.ts | 5 ++-- spec/base.spec.ts | 11 --------- spec/import.spec.ts | 45 ----------------------------------- spec/render.spec.ts | 58 ++++++++++++++++++++------------------------- 8 files changed, 32 insertions(+), 120 deletions(-) delete mode 100644 source/import.ts delete mode 100644 spec/import.spec.ts diff --git a/package.json b/package.json index ef4a51e..4fe47ce 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,10 @@ "types": "./lib/index.d.ts", "default": "./lib/index.js" }, + "./render": { + "types": "./lib/render.d.ts", + "default": "./lib/render.js" + }, "./package.json": "./package.json" }, "scripts": { diff --git a/source/base.ts b/source/base.ts index d8fa9df..160cf44 100644 --- a/source/base.ts +++ b/source/base.ts @@ -14,13 +14,10 @@ */ import { jsonify } from '#jsonify'; -import { renderError } from '#render'; import { $cause, $meta, $namespace, $tags } from '#symbols'; import type { JsonObject } from 'type-fest'; -import type { RenderOptions } from '#render'; - export interface XceptionOptions { /** upstream error */ cause?: unknown; @@ -98,15 +95,6 @@ export class Xception extends Error { return this[$tags]; } - /** - * render the error to a string - * @param options optional parameters - * @returns a rendered string to print - */ - public render(options?: RenderOptions): string { - return renderError(this, options); - } - /** * convert the error to a jsonifiable object * @returns a jsonifiable object diff --git a/source/import.ts b/source/import.ts deleted file mode 100644 index dc79eab..0000000 --- a/source/import.ts +++ /dev/null @@ -1,16 +0,0 @@ -/** - * get the file system module to avoid loading helpers from fs in a browser environment - * @returns the file system module - */ -export async function importFs(): Promise<{ - existsSync: (path: string) => boolean; - readFileSync: (path: string) => Buffer; -}> { - const { existsSync, readFileSync } = - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - globalThis.process?.versions.node - ? await import('node:fs') - : { existsSync: () => false, readFileSync: () => Buffer.from('') }; - - return { existsSync, readFileSync }; -} diff --git a/source/index.ts b/source/index.ts index ca956de..ad013ce 100644 --- a/source/index.ts +++ b/source/index.ts @@ -18,5 +18,4 @@ export * from './base'; export { Xception as default } from './base'; -export * from './render'; export { default as xception } from './xception'; diff --git a/source/render.ts b/source/render.ts index f6305a8..2303892 100644 --- a/source/render.ts +++ b/source/render.ts @@ -13,11 +13,12 @@ * ------------------------------------------------------------------------- */ +import { existsSync, readFileSync } from 'node:fs'; + import chalk from 'chalk'; import highlight from 'highlight-es'; import yamlify from 'yamlify-object'; -import { importFs } from '#import'; import { jsonify } from '#jsonify'; import { disassembleStack } from '#stack'; @@ -41,8 +42,6 @@ export interface RenderOptions { filter?: (path: string) => boolean; } -const { existsSync, readFileSync } = await importFs(); - const PADDING = ' '; /** default number of lines from the targeted source line to be displayed */ diff --git a/spec/base.spec.ts b/spec/base.spec.ts index 599e013..aa52c4a 100644 --- a/spec/base.spec.ts +++ b/spec/base.spec.ts @@ -18,8 +18,6 @@ import { describe, expect, it } from 'vitest'; import { Xception } from '#base'; import { $cause, $meta, $namespace, $tags } from '#symbols'; -import { ansi } from './ansi'; - class NewError extends Xception { constructor(options?: { cause?: unknown }) { super('new error', { ...options, tags: ['new'] }); @@ -112,15 +110,6 @@ describe('cl:Xception', () => { }); }); - describe('render', () => { - it('should render the error', () => { - const rendered = extendedError.render().replace(ansi, ''); - - expect(rendered).toContain('[Xception] extended'); - expect(rendered).toContain('at'); - }); - }); - describe('toJSON', () => { it('should return a jsonifiable object', () => { expect(new Xception('message').toJSON()).toEqual({ diff --git a/spec/import.spec.ts b/spec/import.spec.ts deleted file mode 100644 index ea39cd8..0000000 --- a/spec/import.spec.ts +++ /dev/null @@ -1,45 +0,0 @@ -/* - * *** MIT LICENSE *** - * ------------------------------------------------------------------------- - * This code may be modified and distributed under the MIT license. - * See the LICENSE file for details. - * ------------------------------------------------------------------------- - * - * @summary Tests on importing - * - * @author Alvis HT Tang - * @license MIT - * @copyright Copyright (c) 2020 - All Rights Reserved. - * ------------------------------------------------------------------------- - */ - -import { describe, expect, it, vi } from 'vitest'; - -import { importFs } from '#import'; - -const { importNodeFs } = vi.hoisted(() => ({ - importNodeFs: vi.fn(() => ({ - existsSync: (path: string) => true, - readFileSync: (path: string) => Buffer.from(''), - })), -})); -vi.mock('node:fs', importNodeFs); - -describe('importFs', () => { - it('should import node:fs under a node environment', async () => { - await importFs(); - - expect(importNodeFs).toHaveBeenCalled(); - }); - - it('should not import node:fs under a browser environment', async () => { - vi.stubGlobal('process', { versions: {} }); - - const { existsSync, readFileSync } = await importFs(); - - expect(importNodeFs).not.toHaveBeenCalled(); - - expect(existsSync('/')).toEqual(false); - expect(readFileSync('/')).toEqual(Buffer.from('')); - }); -}); diff --git a/spec/render.spec.ts b/spec/render.spec.ts index c5d4c25..8eeefdd 100644 --- a/spec/render.spec.ts +++ b/spec/render.spec.ts @@ -15,34 +15,34 @@ import { describe, expect, it, vi } from 'vitest'; +import { Xception } from '#base'; +import { renderError } from '#render'; import { ansi } from './ansi'; -vi.mock('#import', () => ({ - importFs: async () => ({ - existsSync(path: string) { - switch (path) { - case 'src1': - case 'src2': - return true; - default: - return false; - } - }, - readFileSync(path: string) { - console.log('reading: ', path); - - switch (path) { - case 'src1': - case 'src2': - return new Array(20) - .fill(undefined) - .map((_, index) => `line ${index + 1}`) - .join('\n'); - default: - throw new Error(`unrecognized path: ${path}`); - } - }, - }), +vi.mock('node:fs', () => ({ + existsSync(path: string) { + switch (path) { + case 'src1': + case 'src2': + return true; + default: + return false; + } + }, + readFileSync(path: string) { + console.log('reading: ', path); + + switch (path) { + case 'src1': + case 'src2': + return new Array(20) + .fill(undefined) + .map((_, index) => `line ${index + 1}`) + .join('\n'); + default: + throw new Error(`unrecognized path: ${path}`); + } + }, })); class MockedError extends Error { @@ -55,12 +55,6 @@ class MockedError extends Error { } } -import { Xception } from '#base'; -import { renderError } from '#render'; - -// const { Xception } = await import('#base'); -// const { renderError } = await import('#render'); - describe('fn:renderError', () => { it('should render an error stack with its own format', () => { const rendered = renderError(