Skip to content

feat: allow for @cypress/webpack-batteries-included-preprocessor to fully resolve the tsconfig.json for compilerOptions in ts-loader (v15) #31590

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

Open
wants to merge 1 commit into
base: release/15.0.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .circleci/workflows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ mainBuildFilters: &mainBuildFilters
- /^release\/\d+\.\d+\.\d+$/
# use the following branch as well to ensure that v8 snapshot cache updates are fully tested
- 'update-v8-snapshot-cache-on-develop'
- 'remove-migration'
- 'feat/wbip_full_resolve_ts_config'

# usually we don't build Mac app - it takes a long time
# but sometimes we want to really confirm we are doing the right thing
Expand All @@ -49,7 +49,7 @@ macWorkflowFilters: &darwin-workflow-filters
- equal: [ develop, << pipeline.git.branch >> ]
# use the following branch as well to ensure that v8 snapshot cache updates are fully tested
- equal: [ 'update-v8-snapshot-cache-on-develop', << pipeline.git.branch >> ]
- equal: [ 'remove-migration', << pipeline.git.branch >> ]
- equal: [ 'feat/wbip_full_resolve_ts_config', << pipeline.git.branch >> ]
- matches:
pattern: /^release\/\d+\.\d+\.\d+$/
value: << pipeline.git.branch >>
Expand All @@ -60,7 +60,7 @@ linuxArm64WorkflowFilters: &linux-arm64-workflow-filters
- equal: [ develop, << pipeline.git.branch >> ]
# use the following branch as well to ensure that v8 snapshot cache updates are fully tested
- equal: [ 'update-v8-snapshot-cache-on-develop', << pipeline.git.branch >> ]
- equal: [ 'remove-migration', << pipeline.git.branch >> ]
- equal: [ 'feat/wbip_full_resolve_ts_config', << pipeline.git.branch >> ]
- matches:
pattern: /^release\/\d+\.\d+\.\d+$/
value: << pipeline.git.branch >>
Expand All @@ -83,7 +83,7 @@ windowsWorkflowFilters: &windows-workflow-filters
- equal: [ develop, << pipeline.git.branch >> ]
# use the following branch as well to ensure that v8 snapshot cache updates are fully tested
- equal: [ 'update-v8-snapshot-cache-on-develop', << pipeline.git.branch >> ]
- equal: [ 'remove-migration', << pipeline.git.branch >> ]
- equal: [ 'feat/wbip_full_resolve_ts_config', << pipeline.git.branch >> ]
- matches:
pattern: /^release\/\d+\.\d+\.\d+$/
value: << pipeline.git.branch >>
Expand Down Expand Up @@ -157,7 +157,7 @@ commands:
name: Set environment variable to determine whether or not to persist artifacts
command: |
echo "Setting SHOULD_PERSIST_ARTIFACTS variable"
echo 'if ! [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "release/"* && "$CIRCLE_BRANCH" != "remove-migration" ]]; then
echo 'if ! [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "release/"* && "$CIRCLE_BRANCH" != "feat/wbip_full_resolve_ts_config" ]]; then
export SHOULD_PERSIST_ARTIFACTS=true
fi' >> "$BASH_ENV"
# You must run `setup_should_persist_artifacts` command and be using bash before running this command
Expand Down
1 change: 1 addition & 0 deletions npm/vite-dev-server/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"resolveJsonModule": true,
"target": "ES2017" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */,
"module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */,
"moduleResolution": "node",
"lib": [
"es2015",
"dom"
Expand Down
30 changes: 23 additions & 7 deletions npm/webpack-batteries-included-preprocessor/index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
const path = require('path')
const webpack = require('webpack')
const Debug = require('debug')
const getTsConfig = require('get-tsconfig')
const webpackPreprocessor = require('@cypress/webpack-preprocessor')

const debug = Debug('cypress:webpack-batteries-included-preprocessor')

class TsConfigNotFoundError extends Error {
constructor () {
super('No tsconfig.json found, but typescript is installed. ts-loader needs a tsconfig.json file to work. Please add one to your project in either the root or the cypress directory.')
this.name = 'TsConfigNotFoundError'
}
}

const hasTsLoader = (rules) => {
return rules.some((rule) => {
if (!rule.use || !Array.isArray(rule.use)) return false
Expand All @@ -16,6 +24,17 @@ const hasTsLoader = (rules) => {
}

const addTypeScriptConfig = (file, options) => {
// returns null if tsconfig cannot be found in the path/parent hierarchy
const configFile = getTsConfig.getTsconfig(file.filePath)

if (!configFile && typescriptExtensionRegex.test(file.filePath)) {
debug('no user tsconfig.json found. Throwing TsConfigNotFoundError')
// @see https://github.com/cypress-io/cypress/issues/18938
throw new TsConfigNotFoundError()
}

debug(`found user tsconfig.json at ${configFile?.path} with compilerOptions: ${JSON.stringify(configFile?.config?.compilerOptions)}`)

// shortcut if we know we've already added typescript support
if (options.__typescriptSupportAdded) return options

Expand All @@ -35,13 +54,6 @@ const addTypeScriptConfig = (file, options) => {
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin')
// node will try to load a projects tsconfig.json instead of the node

const getTsConfig = require('get-tsconfig')

// returns null if tsconfig cannot be found in the path/parent hierarchy
const configFile = getTsConfig.getTsconfig(file.filePath)

configFile ? debug(`found user tsconfig.json at ${configFile?.path} with compilerOptions: ${JSON.stringify(configFile?.config?.compilerOptions)}`) : debug('no user tsconfig.json found')

webpackOptions.module.rules.push({
test: /\.tsx?$/,
exclude: [/node_modules/],
Expand All @@ -50,6 +62,10 @@ const addTypeScriptConfig = (file, options) => {
loader: require.resolve('ts-loader'),
options: {
compiler: options.typescript,
// pass in the resolved compiler options from the tsconfig file into ts-loader to most accurately transpile the code
...(configFile ? {
compilerOptions: configFile.config.compilerOptions,
} : {}),
logLevel: 'error',
silent: true,
transpileOnly: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,16 @@ describe('webpack-batteries-included-preprocessor', () => {
mock.stop('@cypress/webpack-preprocessor')
})

it('always returns loader options even if there is an error discovering the user\'s tsconfig.json', () => {
getTsConfigMock.returns(null)
it('correctly passes the options in the user\'s tsconfig.json options into ts-loader', () => {
getTsConfigMock.returns({
config: {
compilerOptions: {
module: 'ESNext',
moduleResolution: 'Bundler',
},
path: '/foo/tsconfig.json',
},
})

const preprocessorCB = preprocessor({
typescript: true,
Expand All @@ -89,8 +97,28 @@ describe('webpack-batteries-included-preprocessor', () => {
expect(tsLoader.options.silent).to.be.true
expect(tsLoader.options.transpileOnly).to.be.true

// compilerOptions are set by `@cypress/webpack-preprocessor` if ts-loader is present
expect(tsLoader.options.compilerOptions).to.be.undefined
// compilerOptions are overridden (sourceMap=true) by `@cypress/webpack-preprocessor` if ts-loader is present
expect(tsLoader.options.compilerOptions).to.deep.equal({
module: 'ESNext',
moduleResolution: 'Bundler',
})
})

// @see https://github.com/cypress-io/cypress/issues/18938. ts-loader needs a tsconfig.json file to work.
it('throws an error if the user\'s tsconfig.json is not found', () => {
getTsConfigMock.returns(null)

const preprocessorCB = preprocessor({
typescript: true,
webpackOptions,
})

expect(() => {
return preprocessorCB({
filePath: 'foo.ts',
outputPath: '.js',
})
}).to.throw('No tsconfig.json found, but typescript is installed. ts-loader needs a tsconfig.json file to work. Please add one to your project in either the root or the cypress directory.')
})
})
})
1 change: 1 addition & 0 deletions npm/webpack-dev-server/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"resolveJsonModule": true,
"target": "ES2017" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */,
"module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */,
"moduleResolution": "node",
"lib": [
"es2015",
"dom"
Expand Down
115 changes: 115 additions & 0 deletions system-tests/__snapshots__/no_tsconfig_ts_js_mix.ts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
exports['e2e no tsconfig ts js mix / fails with no tsconfig.json error'] = `

====================================================================================================

(Run Starting)

┌────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Cypress: 1.2.3 │
│ Browser: FooBrowser 88 │
│ Specs: 1 found (spec.cy.ts) │
│ Searched: cypress/e2e/spec.cy.ts │
└────────────────────────────────────────────────────────────────────────────────────────────────┘


────────────────────────────────────────────────────────────────────────────────────────────────────

Running: spec.cy.ts (1 of 1)

Oops...we found an error preparing this test file:

> cypress/e2e/spec.cy.ts

The error was:

TsConfigNotFoundError: No tsconfig.json found, but typescript is installed. ts-loader needs a tsconfig.json file to work. Please add one to your project in either the root or the cypress directory.
[stack trace lines]

This occurred while Cypress was compiling and bundling your test code. This is usually caused by:

- A missing file or dependency
- A syntax error in the file or one of its dependencies

Fix the error in your code and re-run your tests.

(Results)

┌────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Tests: 0 │
│ Passing: 0 │
│ Failing: 1 │
│ Pending: 0 │
│ Skipped: 0 │
│ Screenshots: 0 │
│ Video: false │
│ Duration: X seconds │
│ Spec Ran: spec.cy.ts │
└────────────────────────────────────────────────────────────────────────────────────────────────┘


====================================================================================================

(Run Finished)


Spec Tests Passing Failing Pending Skipped
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
│ ✖ spec.cy.ts XX:XX - - 1 - - │
└────────────────────────────────────────────────────────────────────────────────────────────────┘
✖ 1 of 1 failed (100%) XX:XX - - 1 - -


`

exports['e2e no tsconfig ts js mix / passes'] = `

====================================================================================================

(Run Starting)

┌────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Cypress: 1.2.3 │
│ Browser: FooBrowser 88 │
│ Specs: 1 found (spec.cy.js) │
│ Searched: cypress/e2e/spec.cy.js │
└────────────────────────────────────────────────────────────────────────────────────────────────┘


────────────────────────────────────────────────────────────────────────────────────────────────────

Running: spec.cy.js (1 of 1)


✓ is true

1 passing


(Results)

┌────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Tests: 1 │
│ Passing: 1 │
│ Failing: 0 │
│ Pending: 0 │
│ Skipped: 0 │
│ Screenshots: 0 │
│ Video: false │
│ Duration: X seconds │
│ Spec Ran: spec.cy.js │
└────────────────────────────────────────────────────────────────────────────────────────────────┘


====================================================================================================

(Run Finished)


Spec Tests Passing Failing Pending Skipped
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
│ ✔ spec.cy.js XX:XX 1 1 - - - │
└────────────────────────────────────────────────────────────────────────────────────────────────┘
✔ All specs passed! XX:XX 1 1 - - -


`
7 changes: 7 additions & 0 deletions system-tests/projects/no-tsconfig-ts-js-mix/cypress.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module.exports = {
e2e: {
setupNodeEvents (on, config) {
return config
},
},
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
it('is true', () => {
expect(true).to.be.true
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
it('is true', () => {
expect(true).to.be.true
})
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// intentionally left empty
7 changes: 7 additions & 0 deletions system-tests/projects/no-tsconfig-ts-js-mix/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "no-tsconfig-ts-js-mix",
"version": "0.0.0-test",
"devDependencies": {
"typescript": "^5.6.0"
}
}
8 changes: 8 additions & 0 deletions system-tests/projects/no-tsconfig-ts-js-mix/yarn.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1


typescript@^5.6.0:
version "5.8.3"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.8.3.tgz#92f8a3e5e3cf497356f4178c34cd65a7f5e8440e"
integrity sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==
25 changes: 25 additions & 0 deletions system-tests/test/no_tsconfig_ts_js_mix.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import systemTests from '../lib/system-tests'

describe('e2e no tsconfig ts js mix', () => {
systemTests.setup()

systemTests.it('passes', {
spec: 'spec.cy.js',
browser: 'chrome',
project: 'no-tsconfig-ts-js-mix',
snapshot: true,
})

systemTests.it('fails with no tsconfig.json error', {
spec: 'spec.cy.ts',
browser: 'chrome',
project: 'no-tsconfig-ts-js-mix',
snapshot: true,
expectedExitCode: 1,
async onRun (exec, browserName) {
const { stdout } = await exec()

expect(stdout).to.include('TsConfigNotFoundError: No tsconfig.json found, but typescript is installed. ts-loader needs a tsconfig.json file to work. Please add one to your project in either the root or the cypress directory.')
},
})
})