Skip to content

Commit

Permalink
fix: changes to correct typescript detection behavior (#22058)
Browse files Browse the repository at this point in the history
* fix: changes to correct typescript detection behavior, fixes #22034

Co-authored-by: Matt Henkes <mjhenkes@gmail.com>
  • Loading branch information
tgriesser and mjhenkes committed Jun 2, 2022
1 parent 3b5a245 commit 309c31f
Show file tree
Hide file tree
Showing 11 changed files with 109 additions and 23 deletions.
6 changes: 3 additions & 3 deletions packages/data-context/src/data/ProjectLifecycleManager.ts
Expand Up @@ -619,13 +619,13 @@ export class ProjectLifecycleManager {
}

try {
const packageJson = this.ctx.fs.readJsonSync(this._pathToFile('package.json'))
const pkgJson = this.ctx.fs.readJsonSync(this._pathToFile('package.json'))

if (packageJson.type === 'module') {
if (pkgJson.type === 'module') {
metaState.isProjectUsingESModules = true
}

metaState.isUsingTypeScript = detectLanguage(this.projectRoot, packageJson) === 'ts'
metaState.isUsingTypeScript = detectLanguage({ projectRoot: this.projectRoot, pkgJson, isMigrating: metaState.hasLegacyCypressJson }) === 'ts'
} catch {
// No need to handle
}
Expand Down
34 changes: 27 additions & 7 deletions packages/scaffold-config/src/detect.ts
Expand Up @@ -83,27 +83,27 @@ export function detectFramework (projectPath: string): DetectFramework {
* HAS TYPESCRIPT
* ELSE
* DOES NOT HAVE TYPESCRIPT
* ELSE IF `typescript` dependency in `package.json` OR `tsconfig.json` in `projectRoot/*`
* ELSE IF `typescript` dependency in `package.json` AND `tsconfig.json` in `projectRoot/*`
* HAS TYPESCRIPT
* ELSE
* DOES NOT HAVE TYPESCRIPT
* END
* ELSE IF HAS CYPRESS_JSON
* IF cypress/* contains *.ts file
* IF cypress/* contains non-dts *.ts file
* USE TYPESCRIPT
* ELSE
* DO NOT USE TYPESCRIPT
* END
* ELSE IS NEW PROJECT
* IF `typescript` dependency in `package.json` OR `tsconfig.json` in `projectRoot/*`
* IF `typescript` dependency in `package.json` AND `tsconfig.json` in `projectRoot/*`
* HAS TYPESCRIPT
* ELSE
* DOES NOT HAVE TYPESCRIPT
* END
* END
*/

export function detectLanguage (projectRoot: string, pkgJson: PkgJson): 'js' | 'ts' {
export function detectLanguage ({ projectRoot, pkgJson, isMigrating = false }: { projectRoot: string, pkgJson: PkgJson, isMigrating?: boolean }): 'js' | 'ts' {
try {
if (fs.existsSync(path.join(projectRoot, 'cypress.config.ts'))) {
debug('Detected cypress.config.ts - using TS')
Expand All @@ -120,7 +120,24 @@ export function detectLanguage (projectRoot: string, pkgJson: PkgJson): 'js' | '
debug('Did not find cypress.config file')
}

if ('typescript' in (pkgJson.dependencies || {}) || 'typescript' in (pkgJson.devDependencies || {})) {
// If we can't find an installed TypeScript, there's no way we can assume the project is using TypeScript,
// because it won't work on the next step of installation anyway
try {
const typescriptFile = require.resolve('typescript', { paths: [projectRoot] })

debug('Resolved typescript from %s', typescriptFile)
} catch {
debug('No typescript installed - using js')

return 'js'
}

const allDeps = {
...(pkgJson.dependencies || {}),
...(pkgJson.devDependencies || {}),
}

if ('typescript' in allDeps) {
debug('Detected typescript in package.json - using TS')

return 'ts'
Expand All @@ -131,13 +148,16 @@ export function detectLanguage (projectRoot: string, pkgJson: PkgJson): 'js' | '
}

const globs = [
joinPosix('**/*tsconfig.json'),
joinPosix('cypress', '**/*.{ts,tsx}'),
]

if (!isMigrating) {
globs.push(joinPosix('**/*tsconfig.json'))
}

const tsFiles = globby.sync(globs, { onlyFiles: true, gitignore: true, cwd: projectRoot, ignore: ['node_modules'] })

if (tsFiles.length > 0) {
if (tsFiles.filter((f) => !f.endsWith('.d.ts')).length > 0) {
debug(`Detected ts file(s) ${tsFiles.join(',')} - using TS`)

return 'ts'
Expand Down
4 changes: 3 additions & 1 deletion packages/scaffold-config/test/.mocharc.js
@@ -1 +1,3 @@
module.exports = {}
module.exports = {
watchFiles: ['test/**/*.ts', 'src/**/*.ts'],
}
86 changes: 74 additions & 12 deletions packages/scaffold-config/test/unit/detect.spec.ts
@@ -1,3 +1,4 @@
import Module from 'module'
import { expect } from 'chai'
import fs from 'fs-extra'
import globby from 'globby'
Expand All @@ -6,12 +7,24 @@ import { detectFramework, detectLanguage, PkgJson } from '../../src'
import Fixtures from '@tooling/system-tests'
import path from 'path'

beforeEach(() => {
// @ts-ignore
Module._cache = Object.create(null)
// @ts-ignore
Module._pathCache = Object.create(null)
require.cache = Object.create(null)
})

export async function scaffoldMigrationProject (project: ProjectFixtureDir) {
const projectPath = Fixtures.projectPath(project)

Fixtures.clearFixtureNodeModules(project)

Fixtures.removeProject(project)

await Fixtures.scaffoldProject(project)

return path.join(Fixtures.projectPath(project))
return projectPath
}

interface DepToFake {
Expand Down Expand Up @@ -50,6 +63,11 @@ function fakeDepsInNodeModules (cwd: string, deps: Array<DepToFake | DevDepToFak
path.join(cwd, 'node_modules', depName, 'package.json'),
{ main: 'index.js', version: dep.version },
)

fs.writeFileSync(
path.join(cwd, 'node_modules', depName, 'index.js'),
'export STUB = true',
)
}
}

Expand Down Expand Up @@ -204,36 +222,58 @@ describe('detectFramework', () => {
describe('detectLanguage', () => {
it('existing project with `cypress.config.ts`', async () => {
const projectRoot = await scaffoldMigrationProject('config-with-ts')
const actual = detectLanguage(projectRoot, {} as PkgJson)
const actual = detectLanguage({ projectRoot, pkgJson: {} as PkgJson })

expect(actual).to.eq('ts')
})

it('existing project with `cypress.config.js`', async () => {
const projectRoot = await scaffoldMigrationProject('config-with-js')
const actual = detectLanguage(projectRoot, {} as PkgJson)
const actual = detectLanguage({ projectRoot, pkgJson: {} as PkgJson })

expect(actual).to.eq('js')
})

it('pristine project with typescript in package.json', async () => {
const projectRoot = await scaffoldMigrationProject('pristine-yarn')

fakeDepsInNodeModules(projectRoot, [{ devDependency: 'typescript', version: '4.3.6' }])
const pkgJson = fs.readJsonSync(path.join(projectRoot, 'package.json'))
const actual = detectLanguage(projectRoot, pkgJson)
const actual = detectLanguage({ projectRoot, pkgJson })

expect(actual).to.eq('ts')
})

it('pristine project with root level tsconfig.json', async () => {
const projectRoot = await scaffoldMigrationProject('pristine-npm')
const actual = detectLanguage(projectRoot, {} as PkgJson)

fakeDepsInNodeModules(projectRoot, [{ devDependency: 'typescript', version: '4.3.6' }])
const actual = detectLanguage({ projectRoot, pkgJson: {} as PkgJson })

expect(actual).to.eq('ts')
})

it('detects js if typescript is not resolvable when there is a tsconfig.json', async () => {
let projectRoot = await scaffoldMigrationProject('pristine-npm')

const actual = detectLanguage({ projectRoot, pkgJson: {} as PkgJson })

expect(actual).to.eq('js')

projectRoot = await scaffoldMigrationProject('pristine-npm')

fakeDepsInNodeModules(projectRoot, [{ devDependency: 'typescript', version: '4.3.6' }])

const actualTypescript = detectLanguage({ projectRoot, pkgJson: {} as PkgJson })

expect(actualTypescript).to.eq('ts')
})

it('pre-migration project with tsconfig.json in cypress directory', async () => {
const projectRoot = await scaffoldMigrationProject('migration')
const actual = detectLanguage(projectRoot, {} as PkgJson)

fakeDepsInNodeModules(projectRoot, [{ devDependency: 'typescript', version: '4.3.6' }])
const actual = detectLanguage({ projectRoot, pkgJson: {} as PkgJson })

expect(actual).to.eq('ts')
})
Expand All @@ -252,67 +292,89 @@ describe('detectLanguage', () => {
}
}

it('cypress.json project with only .d.ts files', async () => {
const projectRoot = await scaffoldMigrationProject('migration-dts-files-only')

fakeDepsInNodeModules(projectRoot, [{ devDependency: 'typescript', version: '4.3.6' }])

const actual = detectLanguage({ projectRoot, pkgJson: {} as PkgJson, isMigrating: true })

expect(actual).to.eq('js')
})

it('cypress.json project with a TypeScript supportFile', async () => {
const projectRoot = await scaffoldMigrationProject('migration-ts-files-only')

fakeDepsInNodeModules(projectRoot, [{ devDependency: 'typescript', version: '4.3.6' }])

removeAllTsFilesExcept(projectRoot, 'support')

const actual = detectLanguage(projectRoot, {} as PkgJson)
const actual = detectLanguage({ projectRoot, pkgJson: {} as PkgJson })

expect(actual).to.eq('ts')
})

it('cypress.json project with a TypeScript pluginsFile', async () => {
const projectRoot = await scaffoldMigrationProject('migration-ts-files-only')

fakeDepsInNodeModules(projectRoot, [{ devDependency: 'typescript', version: '4.3.6' }])

removeAllTsFilesExcept(projectRoot, 'plugins')

const actual = detectLanguage(projectRoot, {} as PkgJson)
const actual = detectLanguage({ projectRoot, pkgJson: {} as PkgJson })

expect(actual).to.eq('ts')
})

it('cypress.json project with a TypeScript integration specs', async () => {
const projectRoot = await scaffoldMigrationProject('migration-ts-files-only')

fakeDepsInNodeModules(projectRoot, [{ devDependency: 'typescript', version: '4.3.6' }])

// detected based on `integration/**/*.tsx
removeAllTsFilesExcept(projectRoot, 'integration')

const actual = detectLanguage(projectRoot, {} as PkgJson)
const actual = detectLanguage({ projectRoot, pkgJson: {} as PkgJson })

expect(actual).to.eq('ts')
})

it('cypress.json project with a TypeScript integration spec', async () => {
const projectRoot = await scaffoldMigrationProject('migration-ts-files-only')

fakeDepsInNodeModules(projectRoot, [{ devDependency: 'typescript', version: '4.3.6' }])

// detected based on `integration/**/*.tsx
removeAllTsFilesExcept(projectRoot, 'integration')

const actual = detectLanguage(projectRoot, {} as PkgJson)
const actual = detectLanguage({ projectRoot, pkgJson: {} as PkgJson })

expect(actual).to.eq('ts')
})

it('cypress.json project with a TypeScript commponent spec', async () => {
const projectRoot = await scaffoldMigrationProject('migration-ts-files-only')

fakeDepsInNodeModules(projectRoot, [{ devDependency: 'typescript', version: '4.3.6' }])

// detected based on `integration/**/*.tsx
removeAllTsFilesExcept(projectRoot, 'component')

const actual = detectLanguage(projectRoot, {} as PkgJson)
const actual = detectLanguage({ projectRoot, pkgJson: {} as PkgJson })

expect(actual).to.eq('ts')
})

it('ignores node_modules when checking for tsconfig.json', async () => {
const projectRoot = await scaffoldMigrationProject('pristine-cjs-project')

fakeDepsInNodeModules(projectRoot, [{ devDependency: 'typescript', version: '4.3.6' }])

await fs.mkdirp(path.join(projectRoot, 'node_modules', 'some-node-module'))
await fs.writeFile(path.join(projectRoot, 'node_modules', 'some-node-module', 'tsconfig.json'), '')
const pkgJson = fs.readJsonSync(path.join(projectRoot, 'package.json'))

const actual = detectLanguage(projectRoot, pkgJson)
const actual = detectLanguage({ projectRoot, pkgJson })

expect(actual).to.eq('js')
})
Expand Down
@@ -0,0 +1 @@
{}
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
@@ -0,0 +1 @@
{}

3 comments on commit 309c31f

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on 309c31f Jun 2, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Circle has built the linux x64 version of the Test Runner.

Learn more about this pre-release platform-specific build at https://on.cypress.io/installing-cypress#Install-pre-release-version.

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/10.0.2/linux-x64/develop-309c31f89905809d9a4122a86e5d289e9f6c95a1/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on 309c31f Jun 2, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Circle has built the darwin x64 version of the Test Runner.

Learn more about this pre-release platform-specific build at https://on.cypress.io/installing-cypress#Install-pre-release-version.

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/10.0.2/darwin-x64/develop-309c31f89905809d9a4122a86e5d289e9f6c95a1/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on 309c31f Jun 2, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Circle has built the win32 x64 version of the Test Runner.

Learn more about this pre-release platform-specific build at https://on.cypress.io/installing-cypress#Install-pre-release-version.

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/10.0.2/win32-x64/develop-309c31f89905809d9a4122a86e5d289e9f6c95a1/cypress.tgz

Please sign in to comment.