jmcdo29 / nest-commander

A module for using NestJS to build up CLI applications
https://nest-commander.jaymcdoniel.dev/
MIT License
428 stars 53 forks source link

Error: You need to run with a version of node that supports ES Modules in the VM API #1077

Closed seeren closed 9 months ago

seeren commented 9 months ago

Is there an existing issue that is already proposing this?

Potential Commit/PR that introduced the regression

65f5abce1e04528fa8a70b2a351c904e96db1fc2

Versions

3.12.2 -> 3.12.3

Describe the regression

Jest build fail when import 'nest-commander' members with following error:

 RUNS  src/app/core/questions/confirm.question.spec.ts
/apps/my-project/node_modules/jest-util/build/invariant.js:16
    throw new Error(message);
          ^
Error: You need to run with a version of node that supports ES Modules in the VM API. See https://jestjs.io/docs/ecmascript-modules
    at invariant (/apps/aftersales-api/node_modules/jest-util/build/invariant.js:16:11)
    at importModuleDynamically (/apps/aftersales-api/node_modules/jest-runtime/build/index.js:1511:37)
    at importModuleDynamicallyWrapper (node:internal/vm/module:428:21)
    at importModuleDynamicallyCallback (node:internal/modules/esm/utils:85:14)
    at Object.<anonymous> (/apps/aftersales-api/node_modules/nest-commander/node_modules/prettier/index.cjs:593:23)
    at Runtime._execModule (/apps/aftersales-api/node_modules/jest-runtime/build/index.js:1439:24)
    at Runtime._loadModule (/apps/aftersales-api/node_modules/jest-runtime/build/index.js:1022:12)
    at Runtime.requireModule (/apps/aftersales-api/node_modules/jest-runtime/build/index.js:882:12)
    at Runtime.requireModuleOrMock (/apps/aftersales-api/node_modules/jest-runtime/build/index.js:1048:21)
    at Object.<anonymous> (/apps/aftersales-api/node_modules/nest-commander/node_modules/@fig/complete-commander/lib/index.js:16:36)
    at Runtime._execModule (/apps/aftersales-api/node_modules/jest-runtime/build/index.js:1439:24)
    at Runtime._loadModule (/apps/aftersales-api/node_modules/jest-runtime/build/index.js:1022:12)
    at Runtime.requireModule (/apps/aftersales-api/node_modules/jest-runtime/build/index.js:882:12)
    at Runtime.requireModuleOrMock (/apps/aftersales-api/node_modules/jest-runtime/build/index.js:1048:21)
    at Object.<anonymous> (/apps/aftersales-api/packages/nest-commander/src/completion.factory.ts:2:1)
    at Runtime._execModule (/apps/aftersales-api/node_modules/jest-runtime/build/index.js:1439:24)
    at Runtime._loadModule (/apps/aftersales-api/node_modules/jest-runtime/build/index.js:1022:12)
    at Runtime.requireModule (/apps/aftersales-api/node_modules/jest-runtime/build/index.js:882:12)
    at Runtime.requireModuleOrMock (/apps/aftersales-api/node_modules/jest-runtime/build/index.js:1048:21)
    at Object.<anonymous> (/apps/aftersales-api/packages/nest-commander/src/command.factory.ts:14:1)
    at Runtime._execModule (/apps/aftersales-api/node_modules/jest-runtime/build/index.js:1439:24)
    at Runtime._loadModule (/apps/aftersales-api/node_modules/jest-runtime/build/index.js:1022:12)
    at Runtime.requireModule (/apps/aftersales-api/node_modules/jest-runtime/build/index.js:882:12)
    at Runtime.requireModuleOrMock (/apps/aftersales-api/node_modules/jest-runtime/build/index.js:1048:21)
    at Object.<anonymous> (/apps/aftersales-api/packages/nest-commander/src/index.ts:3:1)
    at Runtime._execModule (/apps/aftersales-api/node_modules/jest-runtime/build/index.js:1439:24)
    at Runtime._loadModule (/apps/aftersales-api/node_modules/jest-runtime/build/index.js:1022:12)
    at Runtime.requireModule (/apps/aftersales-api/node_modules/jest-runtime/build/index.js:882:12)
    at Runtime.requireModuleOrMock (/apps/aftersales-api/node_modules/jest-runtime/build/index.js:1048:21)
    at Object.<anonymous> (/apps/aftersales-api/src/app/core/questions/confirm.question.ts:13:26)
    at Runtime._execModule (/apps/aftersales-api/node_modules/jest-runtime/build/index.js:1439:24)
    at Runtime._loadModule (/apps/aftersales-api/node_modules/jest-runtime/build/index.js:1022:12)
    at Runtime.requireModule (/apps/aftersales-api/node_modules/jest-runtime/build/index.js:882:12)
    at Runtime.requireModuleOrMock (/apps/aftersales-api/node_modules/jest-runtime/build/index.js:1048:21)
    at Object.<anonymous> (/apps/aftersales-api/src/app/core/questions/confirm.question.spec.ts:4:28)
    at Runtime._execModule (/apps/aftersales-api/node_modules/jest-runtime/build/index.js:1439:24)
    at Runtime._loadModule (/apps/aftersales-api/node_modules/jest-runtime/build/index.js:1022:12)
    at Runtime.requireModule (/apps/aftersales-api/node_modules/jest-runtime/build/index.js:882:12)
    at jestAdapter (/apps/aftersales-api/node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapter.js:77:13)
    at runTestInternal (/apps/aftersales-api/node_modules/jest-runner/build/runTest.js:367:16)
    at runTest (/apps/aftersales-api/node_modules/jest-runner/build/runTest.js:444:34)

Minimum reproduction code

Every source code that import 'nest-commander' members, a simple example:

import { Question, QuestionSet } from 'nest-commander';

@QuestionSet({ name: 'confirm' })
export class ConfirmQuestion {
  @Question({
    type: 'confirm',
    name: 'confirm',
    message: 'Do you confirm?',
    default: true,
  })
  parseConfirm(val: boolean) {
    return val;
  }
}

Then the error occurs when running tests with jest:

jest --verbose --config ./jest-spec.json

Using configuration file:

{
  "moduleFileExtensions": ["js", "json", "ts"],
  "rootDir": ".",
  "modulePaths": ["./"],
  "testRegex": ".*\\.spec\\.ts$",
  "transform": {
    "^.+\\.(t|j)s$": "ts-jest"
  },
  "clearMocks": true,
  "testEnvironment": "node"
}

Test example:

import { Test, TestingModule } from '@nestjs/testing';

import { ConfirmQuestion } from 'src/app/core/questions/confirm.question';

describe('ConfirmQuestion', () => {
  let module: TestingModule;
  let confirmQuestion: ConfirmQuestion;

  beforeAll(async (): Promise<TestingModule> => {
    module = await Test.createTestingModule({
      providers: [ConfirmQuestion],
    }).compile();
    confirmQuestion = module.get(ConfirmQuestion);
    return module;
  });

  afterAll(async (): Promise<void> => module.close());

  describe('parseConfirm', () => {
    it('Retrieve argument', (): void => {
      expect(confirmQuestion.parseConfirm(true)).toBeTruthy();
    });
  });
});

Expected behavior

I expect no error occurs when running tests with jest

Other

No response

jmcdo29 commented 9 months ago

Can you make a repo I can clone to test with?

mareksuscak commented 9 months ago

CleanShot 2023-12-02 at 01 37 58@2x

We're seeing a similar error here. Also, happens when a command gets imported into a unit test with Jest. This is Node 20 and Jest 29. Seems to be caused by Fig which imports prettier, which uses a dynamic import. I'd recommend to roll the dependency upgrade back in a patch release, then upgrade in a major due to this breakage.

jmcdo29 commented 9 months ago

Please provide a minimum reproduction repository (Git repository/StackBlitz/CodeSandbox project).

why reproductions are required

sgarner commented 9 months ago

I have the same issue. Here is a reproduction on StackBlitz: https://stackblitz.com/edit/nestjs-typescript-starter-dunbvi?file=src%2Fexample.command.spec.ts&view=editor

Run npm run test to see the error.

Note: In my actual project I am witnessing this error just from having a Command be provided by a NestJs module that is under test, even though the Command itself is not the subject of any tests.

Can I suggest that the Fig completion feature should be an optional dependency, only for those who want it?

reg0 commented 9 months ago

Hi I came across the same issue, here's minimum repo which reproduces it:

https://github.com/reg0/nest-command-issue-reproduce/commits/main https://github.com/reg0/nest-command-issue-reproduce/actions/runs/7084465349/job/19278781699

// edit sorry, probably it's very similar to what @sgarner posted above :)

jmcdo29 commented 9 months ago

Thank you all for the reproductions, very helpful in testing out a fix locally.

I think for now, I'm going to move the @fig/commander-completion to an optional import and throw an error if it somehow doesn't get required correctly. From a cursory test, this should fix up the errors from Jest.

jmcdo29 commented 9 months ago

Should be resolved by 3.12.5