Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

After Spec API TypeScript Code breaks the config.ts file - [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".ts" #23141

Open
GeorgeXCV opened this issue Aug 5, 2022 · 54 comments
Labels
prevent-stale mark an issue so it is ignored by stale[bot] topic: typescript Triaged Issue has been routed to backlog. This is not a commitment to have it prioritized by the team.

Comments

@GeorgeXCV
Copy link

GeorgeXCV commented Aug 5, 2022

Current behavior

If I use the code from the snippet here for TypeScript it breaks the project:

TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".ts" for /Users/george/Documents/core-cypress/cypress.config.ts
    at new NodeError (node:internal/errors:372:5)
    at Object.getFileProtocolModuleFormat [as file:] (node:internal/modules/esm/get_format:76:11)
    at defaultGetFormat (node:internal/modules/esm/get_format:118:38)
    at defaultLoad (node:internal/modules/esm/load:21:20)
    at ESMLoader.load (node:internal/modules/esm/loader:407:26)
    at ESMLoader.moduleProvider (node:internal/modules/esm/loader:326:22)
    at new ModuleJob (node:internal/modules/esm/module_job:66:26)
    at ESMLoader.#createModuleJob (node:internal/modules/esm/loader:345:17)
    at ESMLoader.getModuleJob (node:internal/modules/esm/loader:304:34)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
    at async Promise.all (index 0)
    at async ESMLoader.import (node:internal/modules/esm/loader:385:24)
    at async importModuleDynamicallyWrapper (node:internal/vm/module:437:15)
    at async loadFile (/Users/george/Library/Caches/Cypress/10.3.0/Cypress.app/Contents/Resources/app/packages/server/lib/plugins/child/run_require_async_child.js:106:14)
    at async EventEmitter.<anonymous> (/Users/george/Library/Caches/Cypress/10.3.0/Cypress.app/Contents/Resources/app/packages/server/lib/plugins/child/run_require_async_child.js:116:32)

Even just this line breaks the project:
const del = require('del')

Desired behavior

I can run my tests and the videos are deleted if test passed.

Test code to reproduce

/* eslint @typescript-eslint/no-var-requires: "off" */
import { defineConfig } from "cypress";
import { checkGmail, CheckGmailParam } from "../core-cypress/cypress/plugins/checkGmail";
import * as path from "path";
const del = require('del')

export default defineConfig({
  e2e: {
    async setupNodeEvents(on, config) {
      const version = config.env.version || 'development'
      const configFile = await import(path.join(
        config.projectRoot,
        'cypress/config',
        `${version}.json`
      ));
      config.projectId = "5jgpns"
      config.baseUrl = configFile.baseUrl
      config.env = configFile.env
      config.defaultCommandTimeout = 10000
      config.chromeWebSecurity = false
      on("task", {
        async checkGmail(args: CheckGmailParam) {
          return await checkGmail(args);
        },
      });
      on('after:spec', (spec, results) => {
        if (results && results.stats.failures === 0 && results.video) {
          // `del()` returns a promise, so it's important to return it to ensure
          // deleting the video is finished before moving on
          return del(results.video)
        }
      })
      return config
    },
    reporter: 'mochawesome'
  },
});

Cypress Version

10.1.0

Other

Current snippet also isn't 100% TypeScript/Eslint friendly because uses require instead of import and has unused parameter. Something like this should work right?

import { deleteAsync } from 'del'

on('after:spec', async (_spec, results) => {
        if (results && results.stats.failures === 0 && results.video) {
          // `del()` returns a promise, so it's important to return it to ensure
          // deleting the video is finished before moving on
          await deleteAsync(results.video)
        }
})
@cypress-bot cypress-bot bot added the stage: investigating Someone from Cypress is looking into this label Aug 5, 2022
@rockindahizzy
Copy link
Contributor

Right now there doesn't seem to be enough information to reproduce the problem on our end. Unless we receive a reliable reproduction, we'll eventually have to close this issue until we can reproduce it. This does not mean that your issue is not happening - it just means that we do not have a path to move forward.

Please provide a reproducible example of the issue you're encountering. Here are some tips for providing a Short, Self Contained, Correct, Example and our own Troubleshooting Cypress guide.

@cypress-bot cypress-bot bot added stage: awaiting response Potential fix was proposed; awaiting response and removed stage: investigating Someone from Cypress is looking into this labels Aug 11, 2022
@GeorgeXCV
Copy link
Author

Run any test with the above config file or just the module import in config file, super easy to reproduce.

@rockindahizzy
Copy link
Contributor

rockindahizzy commented Aug 12, 2022

Hi @GeorgeXCV, I was able to recreate and fix this issue in this repo: cypress-del-ts

The ticket for my project was adding "type": "module" to package.json.

I also made some slight changes in comparison to what the guide describes.
import {deleteSync} from 'del'; and deleteSync(results.video)
It seems to work, but it would be awesome if you could confirm the changes work for you.

@GeorgeXCV
Copy link
Author

GeorgeXCV commented Aug 12, 2022

Hi @GeorgeXCV, I was able to recreate and fix this issue in this repo: cypress-del-ts

The ticket for my project was adding "type": "module" to package.json.

I also made some slight changes in comparison to what the guide describes. import {deleteSync} from 'del'; and deleteSync(results.video) It seems to work, but it would be awesome if you could confirm the changes work for you.

I get this error:

Your configFile is invalid: /Users/georgeashton/Documents/core/cypress.config.ts: —record

It threw an error when required, check the stack trace below:

ReferenceError: exports is not defined in ES module scope
    at file:///Users/georgeashton/Documents/core/cypress.config.ts:25:23
    at ModuleJob.run (node:internal/modules/esm/module_job:198:25)
    at async Promise.all (index 0)
    at async ESMLoader.import (node:internal/modules/esm/loader:385:24)
    at async importModuleDynamicallyWrapper (node:internal/vm/module:437:15)
    at async loadFile (/Users/georgeashton/Library/Caches/Cypress/10.3.0/Cypress.app/Contents/Resources/app/packages/server/lib/plugins/child/run_require_async_child.js:106:14)
    at async EventEmitter.<anonymous> (/Users/georgeashton/Library/Caches/Cypress/10.3.0/Cypress.app/Contents/Resources/app/packages/server/lib/plugins/child/run_require_async_child.js:116:32)

Line 25 starts here:

on('after:spec', async (_spec, results) => {
        if (results && results.stats.failures === 0 && results.video) {
           // `del()` returns a promise, so it's important to return it to ensure
           // deleting the video is finished before moving on
           deleteSync(results.video)
        }
      })

Also why does Cypress recommend del module? Wouldn't built in fs module work?

import * as fs from "fs"

on('after:spec', async (_spec, results) => {
        if (results && results.stats.failures === 0 && results.video) {
            fs.unlink(results.video, (err) => {
               if (err) throw err
               return
          })
        }
      })

@cypress-bot cypress-bot bot added stage: investigating Someone from Cypress is looking into this and removed stage: awaiting response Potential fix was proposed; awaiting response labels Aug 12, 2022
@mike-plummer
Copy link
Contributor

Hi @GeorgeXCV - could you verify the contents of your tsconfig.json file at your project root? I was able to reproduce the "exports is not defined in ES module scope" issue you reported by removing this portion of the tsconfig.json from @rockindahizzy 's example:

"target": "es2016",
"module": "ES6",     

In order to use imports in the config file the TS compiler will need to know how to handle module resolution which is provided by these keys

@cypress-bot cypress-bot bot added stage: awaiting response Potential fix was proposed; awaiting response and removed stage: investigating Someone from Cypress is looking into this labels Aug 16, 2022
@GeorgeXCV
Copy link
Author

Hi @GeorgeXCV - could you verify the contents of your tsconfig.json file at your project root? I was able to reproduce the "exports is not defined in ES module scope" issue you reported by removing this portion of the tsconfig.json from @rockindahizzy 's example:

"target": "es2016",
"module": "ES6",     

In order to use imports in the config file the TS compiler will need to know how to handle module resolution which is provided by these keys

Strange, I changed my tsconfig.json (previously using es5) but error persists:

{
    "compilerOptions": {
      "target": "es2016",
      "module": "ES6",
      "moduleResolution": "node",
      "esModuleInterop": true,
      "types": ["cypress", "node"]
    },
    "include": ["**/*.ts"]
  }

I am currently using fs.unlink instead using the snippet I posted here as I think that's better, doesn't require external module and don't need to reconfigure my TypeScript project for it.

@cypress-bot cypress-bot bot added stage: investigating Someone from Cypress is looking into this and removed stage: awaiting response Potential fix was proposed; awaiting response labels Aug 18, 2022
@mike-plummer
Copy link
Contributor

@GeorgeXCV , sorry to hear that didn't fix your issue. There must be some other configuration at play here that hasn't been communicated in this issue. Unfortunately, copying config files into the issue here only gives us a partial view that doesn't capture the entirety of your project context. To help us diagnose what's going on it would be very helpful to get a simple reproduction case as a Git repo. Here are some tips for providing a Short, Self Contained, Correct, Example and our own Troubleshooting Cypress guide.

@nagash77 nagash77 assigned lmiller1990 and unassigned mike-plummer Aug 30, 2022
@nagash77
Copy link
Contributor

Unfortunately we have to close this issue due to inactivity. Please comment if there is new information to provide concerning the original issue and we can reopen.

@nagash77 nagash77 closed this as not planned Won't fix, can't repro, duplicate, stale Aug 30, 2022
@CSchulz
Copy link

CSchulz commented Sep 29, 2022

I have encountered the same issue with 10.3.1 and 10.9.0.

It seems to be related to the import itself. 10.9.0 gives a better error output but it doesn't help to solve the issue.

Your configFile is invalid: projects\e2e-tests\cypress.config.ts

It threw an error when required, check the stack trace below:

Error: Cannot find module '@e2e-commons/config'
Require stack:
- project\projects\hus\ph-e2e-vrk\cypress.config.ts
- Cypress\Cache\10.9.0\Cypress\resources\app\node_modules\@packages\server\lib\plugins\child\run_require_async_child.js
- Cypress\Cache\10.9.0\Cypress\resources\app\node_modules\@packages\server\lib\plugins\child\require_async_child.js
    at Function.Module._resolveFilename (node:internal/modules/cjs/loader:933:15)
    at Function.Module._resolveFilename (Cypress\Cache\10.9.0\Cypress\resources\app\node_modules\tsconfig-paths\lib\register.js:76:40)
    at Function.Module._resolveFilename.sharedData.moduleResolveFilenameHook.installedValue [as _resolveFilename] (Cypress\Cache\10.9.0\Cypress\resources\app\node_modules\@cspotcod
e\source-map-support\source-map-support.js:811:30)
    at Function.Module._load (node:internal/modules/cjs/loader:778:27)
    at Module.require (node:internal/modules/cjs/loader:1005:19)
    at require (node:internal/modules/cjs/helpers:102:18)
    at Object.<anonymous> (project\projects\hus\ph-e2e-vrk\cypress.config.ts:1:1)
    at Module._compile (node:internal/modules/cjs/loader:1105:14)
    at Module.m._compile (Cypress\Cache\10.9.0\Cypress\resources\app\node_modules\ts-node\src\index.ts:1618:23)
    at Module._extensions..js (node:internal/modules/cjs/loader:1159:10)
    at Object.require.extensions.<computed> [as .ts] (Cypress\Cache\10.9.0\Cypress\resources\app\node_modules\ts-node\src\index.ts:1621:12)
    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 loadFile (Cypress\Cache\10.9.0\Cypress\resources\app\node_modules\@packages\server\lib\plugins\child\run_require_async_child.js:89:14)

My paths of tsconfig.json seems to get ignored:

"paths": {
  "@e2e-commons": [
    "dist/e2e-commons",
    "node_modules/@e2e-commons"
  ],
  "@e2e-commons/config": [
    "dist/e2e-commons/config",
    "node_modules/@e2e-commons/config"
  ]
}

@nagash77 nagash77 reopened this Sep 29, 2022
@nagash77 nagash77 assigned mjhenkes and unassigned lmiller1990 Sep 29, 2022
@lmiller1990
Copy link
Contributor

lmiller1990 commented Sep 29, 2022

Can you include your full tsconfig.json

Also what is @e2e-commons/config? Can you share the package.json and whatever it is supposed to be pointing to (what's in dist/e2e-commons and node_modules/@e2e-commons/config? I didn't know you could point to multiple things in paths like this, how does it decide which one to use?)

I need more information to reproduce the issue.

@CSchulz
Copy link

CSchulz commented Oct 3, 2022

I think I have found the issue in my setup. We have changed the build process from CJS to module builds. In our case the build of the package @e2e-commons/config also changed. You find CJS in the require stacktrace (see before).

@e2e-commons/config is our shared e2e test setup containing thinks like setupNodeEvents and commons config options.

Typescript takes the first path which can be resolved. More details can be found here, https://www.typescriptlang.org/docs/handbook/module-resolution.html#path-mapping.
But it doesn't matter even if there is only one path included.

I have noticed that it works if I don't use the built package and build it on the fly.

The complete tsconfig.json:

{
    "compilerOptions": {
        "allowSyntheticDefaultImports": true,
        "baseUrl": "../../..",
        "declaration": false,
        "downlevelIteration": true,
        "esModuleInterop": true,
        "experimentalDecorators": true,
        "forceConsistentCasingInFileNames": true,
        "importHelpers": true,
        "lib": [
            "es2020",
            "dom"
        ],
        "module": "es2020",
        "moduleResolution": "node",
        "noFallthroughCasesInSwitch": true,
        "noPropertyAccessFromIndexSignature": false,
        "noImplicitReturns": true,
        "noUnusedLocals": true,
        "noUnusedParameters": true,
        "resolveJsonModule": true,
        "sourceMap": true,
        "strict": true,
        "strictPropertyInitialization": false,
        "target": "es2017",
        "typeRoots": [
            "project/node_modules/@types"
        ],
        "paths": {
            "@e2e-commons": [
                "dist/e2e-commons",
                "node_modules/@e2e-commons"
            ],
            "@e2e-commons/config": [
                "dist/e2e-commons/config",
                "node_modules/@e2e-commons/config"
            ]
        },
        "types": [
            "cypress",
            "node",
        ]
    },
    "include": [
        "**/*.ts"
    ]
}

The resulting package.json from the build looks like the following:

{
  "module": "../fesm2015/e2e-commons-config.mjs",
  "es2020": "../fesm2020/e2e-commons-config.mjs",
  "esm2020": "../esm2020/config/e2e-commons-config.mjs",
  "fesm2020": "../fesm2020/e2e-commons-config.mjs",
  "fesm2015": "../fesm2015/e2e-commons-config.mjs",
  "typings": "e2e-commons-config.d.ts",
  "sideEffects": false,
  "name": "@e2e-commons/config"
}

@ghost
Copy link

ghost commented Oct 4, 2022

I ran into a similar issue yesterday and found a solution (although it may not be applicable to all cases). So, it's not documented anywhere, but looks like this error happens if your tsconfig.json file has compiler options that are not supported by your current Typescript version. In this case it just fails with this extension error and TS itself doesn't provide any hints to indicate that the config is not relevant for the current version

@nagash77 nagash77 removed the stage: investigating Someone from Cypress is looking into this label Jan 10, 2023
@nagash77
Copy link
Contributor

Unfortunately we have to close this issue due to inactivity. Please comment if there is new information to provide concerning the original issue and we can reopen.

@nagash77 nagash77 closed this as not planned Won't fix, can't repro, duplicate, stale Jan 10, 2023
@karlhorky
Copy link
Contributor

karlhorky commented Jan 11, 2023

Hm, seems a bit unusual to close an issue that has a detailed description of a problem that is still open... can't imagine the motivation for closing it. I suppose maybe the Cypress team discussed that it would clean up clutter? But there are more reasons to keep it open:

  • users run into this (very frustrating and confusing) problem, and keeping it open so that they can search for it is useful
  • closing as "unplanned" is actually not accurate - there are planned improvements, unresolved issues and unfinished investigations above
  • closing will lead to duplicate issues being posted, because an issue being "closed" can often mean that a new issue should be posted - which duplicates work (I understand that the post above mentions that new information should be posted here, but this will not always be observed or recognized by users who see a closed issue)

@karlhorky
Copy link
Contributor

karlhorky commented Jan 11, 2023

At the very least, this should stay open until the accepted resolutions which are described above (eg. cypress.config.mts) are merged + published in a release.

Then the authors / commenters in this issue (@GeorgeXCV, @CSchulz, @iamsellek, @nikolayeasygenerator, myself) should also be asked to comment whether the new release resolves their issue.

If they don't respond within a reasonable amount of time (2 weeks?) then closing seems reasonable at that point.

@nagash77
Copy link
Contributor

@karlhorky Sorry, this issue seems to have gotten incorrectly marked and not routed to the correct team and thus looked abandoned. I will reopen and route.

@nagash77 nagash77 reopened this Jan 11, 2023
@nagash77 nagash77 added routed-to-ct CT Issue related to component testing labels Jan 11, 2023
@karlhorky
Copy link
Contributor

Thanks, no worries 👍

@gilliardmacedo
Copy link

I was at the same point of the issue creator. My config was working before, and after trying to include removal of sucessful test videos, it broke.

I repeated many steps of this thread, then I reverted all my work and I only rewrote the function. It worked:

on('after:spec', (spec, results: CypressCommandLine.RunResult) => {
        // delete the video if the spec passed and no tests retried
        if (results && results.video) {
          const failed = results.tests.some((test) => test.state === 'failed')
          if (!failed) {
            unlink(results.video, () => {
              console.log(`${results.video} deleted`)
            })
          }
        }
      })

Seems like the problem appears using lodash or del.

@lmiller1990
Copy link
Contributor

Seems like the problem appears using lodash or del.

del has a similar issue sindresorhus/del#152

Could be unrelated to Cypress - can you post a minimal reproduction? Lodash should be working fine, I tried this with a ESM only project and did not encounter any issues.

I think there is many ways to encounter this issue, some might be an issue in Cypress, some with external modules.

@nagash77 nagash77 added the prevent-stale mark an issue so it is ignored by stale[bot] label Apr 3, 2023
@nagash77 nagash77 added Triaged Issue has been routed to backlog. This is not a commitment to have it prioritized by the team. and removed routed-to-ct labels Apr 19, 2023
@adamalston
Copy link

After I updated my config file using the changes from cypress-io/cypress-documentation#5198, this issue was resolved for me. More specifically, the errors went away when I removed the return and substituted the del usage with its equivalent fs functionality.

@lmiller1990
Copy link
Contributor

Seems there are many issues that can manifest in this error. If anyone finds one, please post a new issue with a minimal reproduction, and we will take a look.

@karlhorky
Copy link
Contributor

@lmiller1990 I don't think that this should be closed.

There is a long, detailed discussion between you and me in this issue which still should be investigated more.

I hope the Cypress team takes this issue seriously and decides to reopen, for the sake of your community.

@karlhorky
Copy link
Contributor

karlhorky commented Jul 5, 2023

At the very least, this should stay open until the accepted resolutions which are described above (eg. cypress.config.mts) are merged + published in a release.

Then the authors / commenters in this issue (@GeorgeXCV, @CSchulz, @iamsellek, @nikolayeasygenerator, myself) should also be asked to comment whether the new release resolves their issue.

@lmiller1990
Copy link
Contributor

lmiller1990 commented Jul 5, 2023

Oh, I'm sorry, I think you are right - I didn't realize this was the issue with the mts discussion. I prematurely closed it. I agree, we should make sure we are in a good place with the whole ESM / CJS / TS loading.

We can track specific progress for cypress.config.mts here: #24962

@lmiller1990 lmiller1990 reopened this Jul 5, 2023
@lmiller1990
Copy link
Contributor

lmiller1990 commented Jul 5, 2023

To follow our original discussion, and give an update since it's been a while since then

... three things to try:

  1. try out using configuration to always transform to ESM
  2. .mts extension
  3. esm flag
  1. I hope we can do this, I would love to just have one way to handle all the things. ES build looks promising, I think it can do ESM and CJS? (need to look into it more). This would be a big change but a welcome one that would likely be a lot of work and take a bit of coordination on the Cypress team, likely too big for a third party contributor unless someone has lot of time on the hands (would highly encourage discussing before coding at all, to make sure a PR would even be practical and accepted, it'll likely be a lot of work to ensure people can smooth migrate, we cannot have a big breaking change, the project is too mature for that).
  2. we should definitely do it, Support cypress.config.mts #24962
  3. I don't think adding more flags is a good idea, it makes the project and loading even more fragmented and confusing.

@karlhorky
Copy link
Contributor

  1. Yeah, esbuild works pretty well for our projects, although to support require() and friends, you need a custom banner configuration
  2. Right, this may be simple and maybe also good enough for fixing most configurations
  3. Agreed that config flag should be last resort, if the other options do not work

@JoseAlban
Copy link

I had the same issue, due to the del import on my cypress.config.ts file, and using the fs.unlink snipped has fixed it for me.

thanks a lot for digging into it

@jennifer-shehane jennifer-shehane added topic: typescript and removed CT Issue related to component testing labels May 15, 2024
@elado
Copy link

elado commented Jun 4, 2024

I had the same issue after upgrading TypeScript to 5.4.5.

export DEBUG=cypress:*

Revealed the underlying issue - it was ts-node not being able to load "extends": "@foo/tsconfig" but only "extends": "./path/to/foo/tsconfig.json". This issue was fixed in ts-node 10.9.2 but my current version of Cypress uses 10.9.1. I changed the extends to a relative path as a workaround.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
prevent-stale mark an issue so it is ignored by stale[bot] topic: typescript Triaged Issue has been routed to backlog. This is not a commitment to have it prioritized by the team.
Projects
None yet
Development

No branches or pull requests