cypress-io / cypress

Fast, easy and reliable testing for anything that runs in a browser.
https://cypress.io
MIT License
46.66k stars 3.16k forks source link

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

Open GeorgeXCV opened 2 years ago

GeorgeXCV commented 2 years ago

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)
        }
})
lmiller1990 commented 1 year ago

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, #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 commented 1 year ago
  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 commented 12 months ago

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

elado commented 3 months ago

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.