node.jsangulartypescriptprotractorcucumberjs

"Error: Cannot use import statement outside a module" in Cucumber-JS step definition w/ typescript


I am getting the following error:

command: npx cucumber-js .\cucumber-e2e\
import { Given, When, Then  } from '@cucumber/cucumber';
^^^^^^
SyntaxError: Cannot use import statement outside a module
at Object.compileFunction (node:vm:352:18)
    at wrapSafe (node:internal/modules/cjs/loader:1032:15)
    at Module._compile (node:internal/modules/cjs/loader:1067:27)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1157:10)
    at Module.load (node:internal/modules/cjs/loader:981:32)
    at Function.Module._load (node:internal/modules/cjs/loader:822:12)
    at Module.require (node:internal/modules/cjs/loader:1005:19)
    at require (node:internal/modules/cjs/helpers:102:18)
    at C:\dev\FrontSystems.KeystonePortal\Keystone.Web\ClientApp\node_modules\@cucumber\cucumber\lib\cli\index.js:122:17
    at Array.forEach (<anonymous>)
    at Cli.getSupportCodeLibrary (C:\dev\xxxxx\xxxx.Web\ClientApp\node_modules\@cucumber\cucumber\lib\cli\index.js:120:26) 
    at Cli.run (C:\dev\xxxx\xxxx.Web\ClientApp\node_modules\@cucumber\cucumber\lib\cli\index.js:145:41)
    at async Object.run [as default] (C:\dev\xxxxx\xxxx.Web\ClientApp\node_modules\@cucumber\cucumber\lib\cli\run.js:25:18)codepath: C:\dev\xxxxx\xxxx.Web\ClientApp\cucumber-e2e\step-definitions\catalog.steps.ts

steps file:

import { Given, When, Then  } from '@cucumber/cucumber';

Given('A bank account with starting balance of {int}', (balance: number) => {
    // Write code here that turns the phrase above into concrete actions
    return 'pending';
  });

My folder structure is the following:

enter image description here

cucumber.js:

var common = [
  '--require ./cucumber-e2e/step-definitions/**/*.ts',
  '--publish-quiet',
].join(' ');

module.exports = {
  default: common,
};

tsconfig.json:

{
  "extends": "../tsconfig.json",
  "compilerOptions": {
    "outDir": "../out-tsc/cucumber-e2e",
    "module": "commonjs",
    "target": "es5",
    "types": [
      "jasmine",
      "jasminewd2",
      "node"
    ]
  }
}

inherited tsconfig.json:

{
  "compileOnSave": false,
  "compilerOptions": {
    "allowSyntheticDefaultImports": true,
    "baseUrl": "./",
    "outDir": "./dist/out-tsc",
    "sourceMap": true,
    "declaration": false,
    "module": "esnext",
    "moduleResolution": "node",
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "importHelpers": true,
    "target": "es2015",
    "resolveJsonModule": true,
    "typeRoots": [
      "node_modules/@types"
    ],
    "lib": [
      "es2018",
      "dom"
    ],
    "paths": {
      "jszip": [
        "node_modules/jszip/dist/jszip.min.js"
      ]
    },
    "plugins": [
      {
        "name": "typescript-tslint-plugin",
        "alwaysShowRuleFailuresAsWarnings": false,
        "ignoreDefinitionFiles": true,
        "configFile": "./tslint.json",
        "suppressWhileTypeErrorsPresent": false
      }
    ]
  }
}

and I've added the following packages to package.json:

"@cucumber/cucumber": "^7.3.2",
"@types/chai": "^4.3.0",
"chai": "^4.3.6",
"chai-as-promised": "^7.1.1",
"protractor-cucumber-framework": "^8.4.0",
"webdriver-manager": "^12.1.8"

So, the feature files and the step definitions are being recognised, however it's throwing a syntax error when it shouldn't. I have a feeling it might be related to the package.json but I've tried multiple versions of the different packages with no positive result.

All tutorials out there seem to do it this way or very similar.

Any ideas?


Solution

  • If you haven't specified the type of module in your package.json, it will default to CommonJS. In this context, you cannot use the import syntax, you have to rely on require.

    There are 2 ways to resolve this:

    1. Change your import syntax to use require:
    const { Given, When, Then } = require('@cucumber/cucumber');
    
    1. Change your module type to an ES module:
    // package.json
    {
      ...
      "type": "module",
      ...
    }
    

    Note that in this second case, if the module you are requesting is a CommonJS module, it may not support named exports and you will have to fallback to the following syntax:

    import Cucumber from '@cucumber/cucumber';
    const { Given, When, Then } = Cucumber;