import fs from 'node:fs/promises'
import path from 'node:path'
import { pathToFileURL } from 'node:url'
import { execSync } from 'node:child_process'
import chalk from 'chalk'
import semver from 'semver'
import { Command } from 'commander'
import { resolve } from 'import-meta-resolve'
import { runProgram, getPackageVersion } from './utils.js'
import {
ASCII_ROBOT, PROGRAM_TITLE, UNSUPPORTED_NODE_VERSION, DEFAULT_NPM_TAG,
INSTALL_COMMAND, DEV_FLAG, SUPPORTED_PACKAGE_MANAGERS, EXECUTER, EXECUTE_COMMAND
} from './constants.js'
import type { ProgramOpts } from './types.js'
const WDIO_COMMAND = 'wdio'
let projectDir: string | undefined
export async function run(operation = createWebdriverIO) {
const version = await getPackageVersion()
* print program ASCII art
*/
if (!(process.argv.includes('--version') || process.argv.includes('-v'))) {
console.log(ASCII_ROBOT, PROGRAM_TITLE)
}
* ensure right Node.js version is used
*/
const unsupportedNodeVersion = !semver.satisfies(process.version, '>=18.18.0')
if (unsupportedNodeVersion) {
console.log(chalk.yellow(UNSUPPORTED_NODE_VERSION))
return
}
const program = new Command(WDIO_COMMAND)
.version(version, '-v, --version')
.arguments('[project-path]')
.usage(`${chalk.green('[project-path]')} [options]`)
.action(name => (projectDir = name))
.option('-t, --npm-tag <tag>', 'Which NPM version you like to install, e.g. @next', DEFAULT_NPM_TAG)
.option('-y, --yes', 'will fill in all config defaults without prompting', false)
.option('-d, --dev', 'Install all packages as into devDependencies', true)
.allowUnknownOption()
.on('--help', () => console.log())
.parse(process.argv)
return operation(program.opts())
}
* detects the package manager that was used
* uses the environment variable `npm_config_user_agent` to detect the package manager
* falls back to `npm` if no package manager could be detected
*/
function detectPackageManager() {
if (!process.env.npm_config_user_agent) {
return 'npm'
}
const detectedPM = process.env.npm_config_user_agent.split('/')[0].toLowerCase()
const matchedPM = SUPPORTED_PACKAGE_MANAGERS.find(pm => pm.toLowerCase() === detectedPM)
return matchedPM || 'npm'
}
export async function createWebdriverIO(opts: ProgramOpts) {
const npmTag = opts.npmTag.startsWith('@') ? opts.npmTag : `@${opts.npmTag}`
const root = path.resolve(process.cwd(), projectDir || '')
* find package manager that was used to create project
*/
const pm = detectPackageManager()
const hasPackageJson = await fs.access(path.resolve(root, 'package.json')).then(() => true).catch(() => false)
if (!hasPackageJson) {
await fs.mkdir(root, { recursive: true })
await fs.writeFile(path.resolve(root, 'package.json'), JSON.stringify({
name: root.substring(root.lastIndexOf(path.sep) + 1),
type: 'module'
}, null, 2))
}
const cliInstalled = await isCLIInstalled(root)
if (!cliInstalled) {
console.log(`\nInstalling ${chalk.bold('@wdio/cli')} to initialize project...`)
const args = [INSTALL_COMMAND[pm]]
if (opts.dev) {
args.push(DEV_FLAG[pm])
}
args.push(`@wdio/cli${npmTag}`)
await runProgram(pm, args, { cwd: root, stdio: 'ignore' })
console.log(chalk.green.bold('✔ Success!'))
}
return runProgram(EXECUTER[pm], [
EXECUTE_COMMAND[pm],
WDIO_COMMAND,
'config',
...(opts.yes ? ['--yes'] : []),
...(opts.npmTag ? ['--npm-tag', opts.npmTag] : [])
].filter(i => !!i), { cwd: root })
}
async function isCLIInstalled(path: string) {
try {
resolve('@wdio/cli', pathToFileURL(path).href)
return true
} catch {
try {
const output = execSync('npm ls -g', {
encoding: 'utf-8',
stdio: ['ignore', 'pipe', 'ignore']
})
if (output.includes('@wdio/cli')) {
return true
}
} catch {
return false
}
return false
}
}