7

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?

Victor
  • 907
  • 2
  • 17
  • 42
  • Can you share the other tsconfig file? Seems like you're extending from it. I've somewhat reproduced your code and I'm not getting the same error. – Vitor EL Mar 23 '22 at 15:00
  • @VitorEL there you go. What error are you gettting by the way? – Victor Mar 23 '22 at 15:04
  • I'm getting the same error even creating a brand new tsconfig.json file – Victor Mar 23 '22 at 15:16
  • 1
    It might be that node is trying to run a ts file as if it were a js file, so your ts file isn't being compiled. Try npm i -D ts-node and then add this line to your var common in cucumber.js '--require-module ts-node/register', – Vitor EL Mar 23 '22 at 16:11
  • @VitorEL Thanks for the response, I tried that but it's returning the same error – Victor Mar 24 '22 at 08:08
  • Might be the inherited tsconfig.json "module": "esnext",. Try changing this to "module": "commonjs", – P.Brian.Mackey Nov 02 '22 at 19:23

1 Answers1

3

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;
Ben
  • 1,331
  • 2
  • 8
  • 15
  • You're right that it works with the const statement, however it feels like a workaround, I should be able to have it working this way – Victor Mar 24 '22 at 08:08
  • 1
    I'm afraid that just the way it works... It has a long history which comes down to the fact that for a very long time, JS did not have a spec for modules. NodeJS implemented CommonJS, then came AMD, UMD and finally ES6 brought the official module system: ES modules. Now the ecosystem is slowly migrating to that ES module syntax but while not everything is ES-compatible, there are some bypasses and workarounds to be used. I'm afraid you won't find a better answer – Ben Mar 24 '22 at 08:31
  • Alright then, you sold me, thank you for your time and knowledge – Victor Mar 24 '22 at 09:23
  • do you have any source that I can check or study regarding this matter? – Victor Mar 24 '22 at 09:24
  • 2
    You can take a look at the MDN docs regarding [Javascript modules](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules) but if you want more detailed history, take a look at that article on Medium [Brief history of JavaScript Modules](https://medium.com/sungthecoder/javascript-module-module-loader-module-bundler-es6-module-confused-yet-6343510e7bde) – Ben Mar 24 '22 at 09:44
  • Just adding onto that, "type":"module" will tell node that your script is using ES Modules instead of CommonJS but you have a typescript project that gets transpilled into javascript and tsconfig.json will determine if its ES Modules or not. That means if your tsconfig is conflicting with your package.json information then you'll have weird issues. ts-node may not work if you have 'type':'module' and imports may fail in the transpiled JS code. So keep an eye up for that. – Vitor EL Mar 24 '22 at 12:58