Browse Source

Improve ESM and CJS support when importing modules in CLI (#430)

* Better error handling

* Better ESM and CJS support when importing modules

* Add changeset
Loris Leiva 9 tháng trước cách đây
mục cha
commit
4ceeb5e6c4

+ 7 - 0
.changeset/heavy-moles-smell.md

@@ -0,0 +1,7 @@
+---
+'codama': patch
+'@codama/errors': patch
+'@codama/cli': patch
+---
+
+Improve ESM and CJS support when importing modules in CLI

+ 1 - 1
packages/cli/package.json

@@ -44,7 +44,7 @@
         "@codama/renderers-rust": "workspace:*",
         "@codama/visitors": "workspace:*",
         "@codama/visitors-core": "workspace:*",
-        "chalk": "^5.4.1",
+        "chalk": "^4.1.2",
         "commander": "^13.1.0",
         "prompts": "^2.4.2"
     },

+ 21 - 11
packages/cli/src/utils/import.ts

@@ -47,20 +47,11 @@ async function importLocalModule<T extends object>(identifier: string, modulePat
     const dotIndex = modulePath.lastIndexOf('.');
     const extension = dotIndex === -1 ? undefined : modulePath.slice(dotIndex);
     const modulePromise = extension === '.json' ? import(modulePath, { with: { type: 'json' } }) : import(modulePath);
-
-    try {
-        return (await modulePromise) as unknown as T;
-    } catch (error) {
-        throw new Error(`Failed to import ${identifier} at "${modulePath}" as a local module`, { cause: error });
-    }
+    return await handleImportPromise(modulePromise, identifier, modulePath);
 }
 
 async function importExternalModule<T extends object>(identifier: string, modulePath: string): Promise<T> {
-    try {
-        return (await import(modulePath)) as unknown as T;
-    } catch (error) {
-        throw new Error(`Failed to import ${identifier} at "${modulePath}" as a module`, { cause: error });
-    }
+    return await handleImportPromise(import(modulePath), identifier, modulePath);
 }
 
 async function importExternalUserModule<T extends object>(identifier: string, modulePath: string): Promise<T> {
@@ -69,3 +60,22 @@ async function importExternalUserModule<T extends object>(identifier: string, mo
     const userModulePath = userRequire.resolve(modulePath);
     return await importExternalModule<T>(identifier, userModulePath);
 }
+
+async function handleImportPromise<T extends object>(
+    importPromise: Promise<unknown>,
+    identifier: string,
+    modulePath: string,
+): Promise<T> {
+    try {
+        return (await importPromise) as T;
+    } catch (error) {
+        let causeMessage =
+            !!error && typeof error === 'object' && 'message' in error && typeof error.message === 'string'
+                ? (error as { message: string }).message
+                : undefined;
+        causeMessage = causeMessage ? ` (caused by: ${causeMessage})` : '';
+        throw new Error(`Failed to import ${identifier} at "${modulePath}" as a module${causeMessage}`, {
+            cause: error,
+        });
+    }
+}

+ 4 - 0
packages/cli/src/utils/logs.ts

@@ -16,6 +16,10 @@ export function logError(...args: unknown[]): void {
     console.log(chalk.red('[Error]'), ...args);
 }
 
+export function logDebug(...args: unknown[]): void {
+    console.log(chalk.magenta('[Debug]'), ...args);
+}
+
 export function logBanner(): void {
     console.log(getBanner());
 }

+ 5 - 0
packages/errors/bin/cli.cjs

@@ -0,0 +1,5 @@
+#!/usr/bin/env -S node
+
+const run = require('../dist/cli.cjs').run;
+
+run(process.argv);

+ 0 - 7
packages/errors/bin/cli.mjs

@@ -1,7 +0,0 @@
-#!/usr/bin/env -S node
-
-import process from 'node:process';
-
-import { run } from '../dist/cli.mjs';
-
-run(process.argv);

+ 1 - 1
packages/errors/package.json

@@ -55,7 +55,7 @@
     "dependencies": {
         "@codama/node-types": "workspace:*",
         "commander": "^13.1.0",
-        "chalk": "^5.4.1"
+        "chalk": "^4.1.2"
     },
     "license": "MIT",
     "repository": {

+ 2 - 2
packages/internals/tsup.config.cli.ts

@@ -4,10 +4,10 @@ import { getBuildConfig } from './tsup.config.base';
 
 export default defineConfig([
     {
-        ...getBuildConfig({ format: 'esm', platform: 'node' }),
+        ...getBuildConfig({ format: 'cjs', platform: 'node' }),
         entry: { cli: './src/cli/index.ts' },
         outExtension() {
-            return { js: `.mjs` };
+            return { js: `.cjs` };
         },
     },
 ]);

+ 5 - 0
packages/library/bin/cli.cjs

@@ -0,0 +1,5 @@
+#!/usr/bin/env -S node
+
+const run = require('../dist/cli.cjs').run;
+
+run(process.argv);

+ 0 - 7
packages/library/bin/cli.mjs

@@ -1,7 +0,0 @@
-#!/usr/bin/env -S node
-
-import process from 'node:process';
-
-import { run } from '../dist/cli.mjs';
-
-run(process.argv);

+ 2 - 2
packages/library/src/cli/index.ts

@@ -1,4 +1,4 @@
-import { createProgram, logError } from '@codama/cli';
+import { createProgram, logDebug, logError } from '@codama/cli';
 
 const program = createProgram();
 
@@ -7,7 +7,7 @@ export async function run(argv: readonly string[]) {
         await program.parseAsync(argv);
     } catch (err) {
         if (program.opts().debug) {
-            logError(`${(err as { stack: string }).stack}`);
+            logDebug(`${(err as { stack: string }).stack}`);
         }
         logError((err as { message: string }).message);
         process.exitCode = 1;

+ 4 - 4
pnpm-lock.yaml

@@ -105,8 +105,8 @@ importers:
         specifier: workspace:*
         version: link:../visitors-core
       chalk:
-        specifier: ^5.4.1
-        version: 5.4.1
+        specifier: ^4.1.2
+        version: 4.1.2
       commander:
         specifier: ^13.1.0
         version: 13.1.0
@@ -161,8 +161,8 @@ importers:
         specifier: workspace:*
         version: link:../node-types
       chalk:
-        specifier: ^5.4.1
-        version: 5.4.1
+        specifier: ^4.1.2
+        version: 4.1.2
       commander:
         specifier: ^13.1.0
         version: 13.1.0