From 309c31f89905809d9a4122a86e5d289e9f6c95a1 Mon Sep 17 00:00:00 2001 From: Tim Griesser Date: Thu, 2 Jun 2022 17:23:58 -0400 Subject: [PATCH] fix: changes to correct typescript detection behavior (#22058) * fix: changes to correct typescript detection behavior, fixes #22034 Co-authored-by: Matt Henkes --- .../src/data/ProjectLifecycleManager.ts | 6 +- packages/scaffold-config/src/detect.ts | 34 ++++++-- packages/scaffold-config/test/.mocharc.js | 4 +- .../scaffold-config/test/unit/detect.spec.ts | 86 ++++++++++++++++--- .../migration-dts-files-only/cypress.json | 1 + .../cypress/component/foo.spec.js | 0 .../cypress/integration/foo.spec.js | 0 .../cypress/plugins/index.js | 0 .../cypress/support/index.d.ts | 0 .../cypress/support/index.js | 0 .../migration-dts-files-only/tsconfig.json | 1 + 11 files changed, 109 insertions(+), 23 deletions(-) create mode 100644 system-tests/projects/migration-dts-files-only/cypress.json create mode 100644 system-tests/projects/migration-dts-files-only/cypress/component/foo.spec.js create mode 100644 system-tests/projects/migration-dts-files-only/cypress/integration/foo.spec.js create mode 100644 system-tests/projects/migration-dts-files-only/cypress/plugins/index.js create mode 100644 system-tests/projects/migration-dts-files-only/cypress/support/index.d.ts create mode 100644 system-tests/projects/migration-dts-files-only/cypress/support/index.js create mode 100644 system-tests/projects/migration-dts-files-only/tsconfig.json diff --git a/packages/data-context/src/data/ProjectLifecycleManager.ts b/packages/data-context/src/data/ProjectLifecycleManager.ts index 645343ad9df4..03de9c2b40c5 100644 --- a/packages/data-context/src/data/ProjectLifecycleManager.ts +++ b/packages/data-context/src/data/ProjectLifecycleManager.ts @@ -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 } diff --git a/packages/scaffold-config/src/detect.ts b/packages/scaffold-config/src/detect.ts index 85ff9323aa1b..6409713a6669 100644 --- a/packages/scaffold-config/src/detect.ts +++ b/packages/scaffold-config/src/detect.ts @@ -83,19 +83,19 @@ 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 @@ -103,7 +103,7 @@ export function detectFramework (projectPath: string): DetectFramework { * 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') @@ -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' @@ -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' diff --git a/packages/scaffold-config/test/.mocharc.js b/packages/scaffold-config/test/.mocharc.js index 4ba52ba2c8df..cbe6157f698f 100644 --- a/packages/scaffold-config/test/.mocharc.js +++ b/packages/scaffold-config/test/.mocharc.js @@ -1 +1,3 @@ -module.exports = {} +module.exports = { + watchFiles: ['test/**/*.ts', 'src/**/*.ts'], +} diff --git a/packages/scaffold-config/test/unit/detect.spec.ts b/packages/scaffold-config/test/unit/detect.spec.ts index 4411ae9215d0..b1b40ba51e6d 100644 --- a/packages/scaffold-config/test/unit/detect.spec.ts +++ b/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' @@ -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 { @@ -50,6 +63,11 @@ function fakeDepsInNodeModules (cwd: string, deps: Array { 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') }) @@ -252,12 +292,24 @@ 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') }) @@ -265,9 +317,11 @@ describe('detectLanguage', () => { 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') }) @@ -275,10 +329,12 @@ describe('detectLanguage', () => { 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') }) @@ -286,10 +342,12 @@ describe('detectLanguage', () => { 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') }) @@ -297,10 +355,12 @@ describe('detectLanguage', () => { 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') }) @@ -308,11 +368,13 @@ describe('detectLanguage', () => { 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') }) diff --git a/system-tests/projects/migration-dts-files-only/cypress.json b/system-tests/projects/migration-dts-files-only/cypress.json new file mode 100644 index 000000000000..9e26dfeeb6e6 --- /dev/null +++ b/system-tests/projects/migration-dts-files-only/cypress.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/system-tests/projects/migration-dts-files-only/cypress/component/foo.spec.js b/system-tests/projects/migration-dts-files-only/cypress/component/foo.spec.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/system-tests/projects/migration-dts-files-only/cypress/integration/foo.spec.js b/system-tests/projects/migration-dts-files-only/cypress/integration/foo.spec.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/system-tests/projects/migration-dts-files-only/cypress/plugins/index.js b/system-tests/projects/migration-dts-files-only/cypress/plugins/index.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/system-tests/projects/migration-dts-files-only/cypress/support/index.d.ts b/system-tests/projects/migration-dts-files-only/cypress/support/index.d.ts new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/system-tests/projects/migration-dts-files-only/cypress/support/index.js b/system-tests/projects/migration-dts-files-only/cypress/support/index.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/system-tests/projects/migration-dts-files-only/tsconfig.json b/system-tests/projects/migration-dts-files-only/tsconfig.json new file mode 100644 index 000000000000..9e26dfeeb6e6 --- /dev/null +++ b/system-tests/projects/migration-dts-files-only/tsconfig.json @@ -0,0 +1 @@ +{} \ No newline at end of file