-
Notifications
You must be signed in to change notification settings - Fork 41
Aps 15770 cypress cli better ts support #993
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
base: master
Are you sure you want to change the base?
Changes from all commits
51b789d
278ed4a
63cc8c5
f56064a
9653cd9
270f295
ce8bd83
df7b87c
36c3dbc
758ee27
b56afdc
999f350
8424da2
d8e3ab2
06bf282
5800b9d
9649e68
f26295e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,6 +13,120 @@ | |
return constants.CYPRESS_V10_AND_ABOVE_CONFIG_FILE_EXTENSIONS.includes(extension) ? extension : 'js' | ||
} | ||
|
||
function resolveTsConfigPath(bsConfig, cypress_config_filepath) { | ||
const working_dir = path.dirname(cypress_config_filepath); | ||
|
||
// Priority order for finding tsconfig | ||
const candidates = [ | ||
bsConfig.run_settings && bsConfig.run_settings.ts_config_file_path, // User specified | ||
path.join(working_dir, 'tsconfig.json'), // Same directory as cypress config | ||
|
||
path.join(working_dir, '..', 'tsconfig.json'), // Parent directory | ||
|
||
path.join(process.cwd(), 'tsconfig.json') // Project root | ||
].filter(Boolean).map(p => path.resolve(p)); | ||
|
||
|
||
for (const candidate of candidates) { | ||
if (fs.existsSync(candidate)) { | ||
logger.debug(`Found tsconfig at: ${candidate}`); | ||
return candidate; | ||
} | ||
} | ||
|
||
return null; | ||
} | ||
|
||
function generateTscCommandAndTempTsConfig(bsConfig, bstack_node_modules_path, complied_js_dir, cypress_config_filepath) { | ||
const working_dir = path.dirname(cypress_config_filepath); | ||
const typescript_path = path.join(bstack_node_modules_path, 'typescript', 'bin', 'tsc'); | ||
const tsc_alias_path = require.resolve('tsc-alias/dist/bin/index.js'); | ||
|
||
// Smart tsconfig detection and validation | ||
const resolvedTsConfigPath = resolveTsConfigPath(bsConfig, cypress_config_filepath); | ||
let hasValidTsConfig = false; | ||
|
||
if (resolvedTsConfigPath) { | ||
try { | ||
// Validate the tsconfig is readable and valid JSON | ||
const tsConfigContent = fs.readFileSync(resolvedTsConfigPath, 'utf8'); | ||
JSON.parse(tsConfigContent); | ||
hasValidTsConfig = true; | ||
logger.info(`Using existing tsconfig: ${resolvedTsConfigPath}`); | ||
} catch (error) { | ||
logger.warn(`Invalid tsconfig file: ${resolvedTsConfigPath}, falling back to default configuration. Error: ${error.message}`); | ||
hasValidTsConfig = false; | ||
} | ||
} else { | ||
logger.info('No tsconfig found, using default TypeScript configuration'); | ||
} | ||
|
||
let tempTsConfig; | ||
|
||
if (hasValidTsConfig) { | ||
// Scenario 1: User has valid tsconfig - use extends approach | ||
tempTsConfig = { | ||
extends: resolvedTsConfigPath, | ||
compilerOptions: { | ||
// Force override critical parameters for BrowserStack compatibility | ||
"outDir": path.basename(complied_js_dir), | ||
"listEmittedFiles": true, | ||
// Ensure these are always set regardless of base tsconfig | ||
"allowSyntheticDefaultImports": true, | ||
"esModuleInterop": true | ||
}, | ||
include: [cypress_config_filepath] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If cypress config imports other TS files, would those be compiled as well? Like one TS file, importing another and so on There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes recursively it will compile, with current master beahviour this is same |
||
}; | ||
} else { | ||
// Scenario 2: No tsconfig or invalid tsconfig - create standalone with all basic parameters | ||
tempTsConfig = { | ||
compilerOptions: { | ||
// Preserve old command-line parameters for backwards compatibility | ||
"outDir": path.basename(complied_js_dir), | ||
"listEmittedFiles": true, | ||
"allowSyntheticDefaultImports": true, | ||
"module": "commonjs", | ||
"declaration": false, | ||
|
||
// Add essential missing parameters for robust compilation | ||
"target": "es2017", | ||
"moduleResolution": "node", | ||
"esModuleInterop": true, | ||
"allowJs": true, | ||
"skipLibCheck": true, | ||
"forceConsistentCasingInFileNames": true, | ||
"resolveJsonModule": true, | ||
"strict": false, // Avoid breaking existing code | ||
"noEmitOnError": false // Continue compilation even with errors | ||
}, | ||
include: [cypress_config_filepath], | ||
exclude: ["node_modules", "dist", "build"] | ||
}; | ||
} | ||
|
||
// Write the temporary tsconfig | ||
const tempTsConfigPath = path.join(working_dir, 'tsconfig.singlefile.tmp.json'); | ||
fs.writeFileSync(tempTsConfigPath, JSON.stringify(tempTsConfig, null, 2)); | ||
logger.info(`Temporary tsconfig created at: ${tempTsConfigPath}`); | ||
|
||
// Platform-specific command generation | ||
const isWindows = /^win/.test(process.platform); | ||
|
||
if (isWindows) { | ||
// Windows: Use && to chain commands, no space after SET | ||
const setNodePath = isWindows | ||
? `set NODE_PATH=${bstack_node_modules_path}` | ||
: `NODE_PATH="${bstack_node_modules_path}"`; | ||
|
||
const tscCommand = `${setNodePath} && node "${typescript_path}" --project "${tempTsConfigPath}" && ${setNodePath} && node "${tsc_alias_path}" --project "${tempTsConfigPath}" --verbose`; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we keep There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes helps in debugging |
||
logger.info(`TypeScript compilation command: ${tscCommand}`); | ||
return { tscCommand, tempTsConfigPath }; | ||
} else { | ||
// Unix/Linux/macOS: Use ; to separate commands or && to chain | ||
const nodePathPrefix = `NODE_PATH=${bstack_node_modules_path}`; | ||
const tscCommand = `${nodePathPrefix} node "${typescript_path}" --project "${tempTsConfigPath}" && ${nodePathPrefix} node "${tsc_alias_path}" --project "${tempTsConfigPath}" --verbose`; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. user can start --cli-debug mode and can see the converted files |
||
logger.info(`TypeScript compilation command: ${tscCommand}`); | ||
return { tscCommand, tempTsConfigPath }; | ||
} | ||
} | ||
|
||
exports.convertTsConfig = (bsConfig, cypress_config_filepath, bstack_node_modules_path) => { | ||
const cypress_config_filename = bsConfig.run_settings.cypress_config_filename | ||
const working_dir = path.dirname(cypress_config_filepath); | ||
|
@@ -22,19 +136,12 @@ | |
} | ||
fs.mkdirSync(complied_js_dir, { recursive: true }) | ||
|
||
const typescript_path = path.join(bstack_node_modules_path, 'typescript', 'bin', 'tsc') | ||
|
||
let tsc_command = `NODE_PATH=${bstack_node_modules_path} node "${typescript_path}" --outDir "${complied_js_dir}" --listEmittedFiles true --allowSyntheticDefaultImports --module commonjs --declaration false "${cypress_config_filepath}"` | ||
const { tscCommand, tempTsConfigPath } = generateTscCommandAndTempTsConfig(bsConfig, bstack_node_modules_path, complied_js_dir, cypress_config_filepath); | ||
|
||
if (/^win/.test(process.platform)) { | ||
tsc_command = `set NODE_PATH=${bstack_node_modules_path}&& node "${typescript_path}" --outDir "${complied_js_dir}" --listEmittedFiles true --allowSyntheticDefaultImports --module commonjs --declaration false "${cypress_config_filepath}"` | ||
} | ||
|
||
|
||
let tsc_output | ||
try { | ||
logger.debug(`Running: ${tsc_command}`) | ||
tsc_output = cp.execSync(tsc_command, { cwd: working_dir }) | ||
logger.debug(`Running: ${tscCommand}`) | ||
tsc_output = cp.execSync(tscCommand, { cwd: working_dir }) | ||
|
||
} catch (err) { | ||
// error while compiling ts files | ||
logger.debug(err.message); | ||
|
@@ -44,6 +151,21 @@ | |
logger.debug(`Saved compiled js output at: ${complied_js_dir}`); | ||
logger.debug(`Finding compiled cypress config file in: ${complied_js_dir}`); | ||
|
||
// Clean up the temporary tsconfig file | ||
if (fs.existsSync(tempTsConfigPath)) { | ||
fs.unlinkSync(tempTsConfigPath); | ||
logger.debug(`Temporary tsconfig file removed: ${tempTsConfigPath}`); | ||
} | ||
|
||
if (tsc_output) { | ||
logger.debug(tsc_output.toString()); | ||
} | ||
|
||
if (!tsc_output) { | ||
logger.error('No TypeScript compilation output available'); | ||
return null; | ||
} | ||
|
||
const lines = tsc_output.toString().split('\n'); | ||
let foundLine = null; | ||
for (let i = 0; i < lines.length; i++) { | ||
|
@@ -53,7 +175,7 @@ | |
} | ||
} | ||
if (foundLine === null) { | ||
logger.error(`No compiled cypress config found. There might some error running ${tsc_command} command`) | ||
logger.error(`No compiled cypress config found. There might some error running ${tscCommand} command`) | ||
return null | ||
} else { | ||
const compiled_cypress_config_filepath = foundLine.split('TSFILE: ').pop() | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, for example someone wants to add any dependency on top of auto import, that won't be allowed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://browserstack.atlassian.net/wiki/spaces/ENG/pages/5015994415/Product+Impact+Analysis+APS-15770#%F0%9F%94%84-Behavior-Changes
Yes this is expected, approved from product