From 1f6011cca1a115bc3f360a95b704bc1c9a1fe0e5 Mon Sep 17 00:00:00 2001 From: Katerina Skroumpelou Date: Fri, 16 Jun 2023 18:55:27 +0300 Subject: [PATCH] fix(js): node executor should always log error --- e2e/js/src/js-node.test.ts | 50 +++++++++++++++++++++ e2e/utils/command-utils.ts | 32 ++++++------- packages/js/src/executors/node/node.impl.ts | 35 ++++++++++++--- 3 files changed, 94 insertions(+), 23 deletions(-) create mode 100644 e2e/js/src/js-node.test.ts diff --git a/e2e/js/src/js-node.test.ts b/e2e/js/src/js-node.test.ts new file mode 100644 index 0000000000000..eda9bf0ddaf02 --- /dev/null +++ b/e2e/js/src/js-node.test.ts @@ -0,0 +1,50 @@ +import { + cleanupProject, + newProject, + runCLI, + uniq, + updateFile, + updateProjectConfig, +} from '@nx/e2e/utils'; + +describe('js:node error handling', () => { + let scope: string; + + beforeEach(() => { + scope = newProject(); + }); + + afterEach(() => cleanupProject()); + + it('should log out the error', () => { + const esbuildLib = uniq('esbuildlib'); + + runCLI( + `generate @nx/js:lib ${esbuildLib} --bundler=esbuild --no-interactive` + ); + + updateFile(`libs/${esbuildLib}/src/index.ts`, () => { + return ` + console.log('Hello from my library!'); + throw new Error('This is an error'); + `; + }); + + updateProjectConfig(esbuildLib, (config) => { + config.targets['run-node'] = { + executor: '@nx/js:node', + options: { + buildTarget: `${esbuildLib}:build`, + watch: false, + }, + }; + return config; + }); + + const output = runCLI(`run ${esbuildLib}:run-node`, { + redirectStderr: true, + }); + expect(output).toContain('Hello from my library!'); + expect(output).toContain('This is an error'); + }, 240_000); +}); diff --git a/e2e/utils/command-utils.ts b/e2e/utils/command-utils.ts index 9146ff67e91a8..3e609435b2513 100644 --- a/e2e/utils/command-utils.ts +++ b/e2e/utils/command-utils.ts @@ -23,6 +23,7 @@ export interface RunCmdOpts { cwd?: string; silent?: boolean; verbose?: boolean; + redirectStderr?: boolean; } /** @@ -325,26 +326,25 @@ export function runCLI( silenceError: false, env: undefined, verbose: undefined, + redirectStderr: undefined, } ): string { try { const pm = getPackageManagerCommand(); - const logs = execSync( - `${pm.runNxSilent} ${command} ${ - opts.verbose ?? isVerboseE2ERun() ? ' --verbose' : '' - }`, - { - cwd: opts.cwd || tmpProjPath(), - env: { - CI: 'true', - ...getStrippedEnvironmentVariables(), - ...opts.env, - }, - encoding: 'utf-8', - stdio: 'pipe', - maxBuffer: 50 * 1024 * 1024, - } - ); + const commandToRun = `${pm.runNxSilent} ${command} ${ + opts.verbose ?? isVerboseE2ERun() ? ' --verbose' : '' + }${opts.redirectStderr ? ' 2>&1' : ''}`; + const logs = execSync(commandToRun, { + cwd: opts.cwd || tmpProjPath(), + env: { + CI: 'true', + ...getStrippedEnvironmentVariables(), + ...opts.env, + }, + encoding: 'utf-8', + stdio: 'pipe', + maxBuffer: 50 * 1024 * 1024, + }); if (opts.verbose ?? isVerboseE2ERun()) { output.log({ diff --git a/packages/js/src/executors/node/node.impl.ts b/packages/js/src/executors/node/node.impl.ts index a186f149174a4..ebfb41819675f 100644 --- a/packages/js/src/executors/node/node.impl.ts +++ b/packages/js/src/executors/node/node.impl.ts @@ -10,11 +10,13 @@ import { import { daemonClient } from 'nx/src/daemon/client/client'; import { randomUUID } from 'crypto'; import { join } from 'path'; +import * as path from 'path'; import { InspectType, NodeExecutorOptions } from './schema'; import { createAsyncIterable } from '@nx/devkit/src/utils/async-iterable'; import { calculateProjectDependencies } from '../../utils/buildable-libs-utils'; import { killTree } from './lib/kill-tree'; +import { fileExists } from 'nx/src/utils/fileutils'; interface ActiveTask { id: string; @@ -71,12 +73,11 @@ export async function* nodeExecutor( // Re-map buildable workspace projects to their output directory. const mappings = calculateResolveMappings(context, options); - const fileToRun = join( - context.root, - buildOptions.outputPath, - buildOptions.outputFileName ?? 'main.js' - ); + const outputFileName = + buildOptions.outputFileName ?? `${path.parse(buildOptions.main).name}.js`; + + const fileToRun = join(context.root, buildOptions.outputPath, outputFileName); const tasks: ActiveTask[] = []; let currentTask: ActiveTask = null; @@ -159,7 +160,7 @@ export async function* nodeExecutor( stdio: [0, 1, 'pipe', 'ipc'], env: { ...process.env, - NX_FILE_TO_RUN: fileToRun, + NX_FILE_TO_RUN: fileToRunCorrectPath(fileToRun), NX_MAPPINGS: JSON.stringify(mappings), }, } @@ -168,7 +169,8 @@ export async function* nodeExecutor( task.childProcess.stderr.on('data', (data) => { // Don't log out error if task is killed and new one has started. // This could happen if a new build is triggered while new process is starting, since the operation is not atomic. - if (options.watch && !task.killed) { + // Log the error in normal mode + if (!options.watch || !task.killed) { logger.error(data.toString()); } }); @@ -304,4 +306,23 @@ function runWaitUntilTargets( ); } +function fileToRunCorrectPath(fileToRun: string): string { + if (!fileExists(fileToRun)) { + const cjsFile = fileToRun.replace(/\.js$/, '.cjs'); + if (fileExists(cjsFile)) { + fileToRun = cjsFile; + } else { + const mjsFile = fileToRun.replace(/\.js$/, '.mjs'); + if (fileExists(mjsFile)) { + fileToRun = mjsFile; + } else { + throw new Error( + `Could not find ${fileToRun}. Make sure your build succeeded.` + ); + } + } + } + return fileToRun; +} + export default nodeExecutor;