letsrunit 0.3.0 → 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.js +2 -2
- package/dist/bin.js.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/package.json +13 -4
package/dist/bin.js
CHANGED
|
@@ -13,7 +13,7 @@ function detectEnvironment() {
|
|
|
13
13
|
let packageManager = "npm";
|
|
14
14
|
if (existsSync(join(cwd, "yarn.lock"))) packageManager = "yarn";
|
|
15
15
|
else if (existsSync(join(cwd, "pnpm-lock.yaml"))) packageManager = "pnpm";
|
|
16
|
-
else if (existsSync(join(cwd, "bun.lockb"))) packageManager = "bun";
|
|
16
|
+
else if (existsSync(join(cwd, "bun.lockb")) || existsSync(join(cwd, "bun.lock"))) packageManager = "bun";
|
|
17
17
|
const nodeVersion = parseInt(process.version.slice(1), 10);
|
|
18
18
|
const hasCucumber = existsSync(join(cwd, "node_modules", "@cucumber", "cucumber", "package.json"));
|
|
19
19
|
return { isInteractive, packageManager, nodeVersion, hasCucumber, cwd };
|
|
@@ -65,7 +65,7 @@ setDefaultTimeout(30_000);
|
|
|
65
65
|
var EXAMPLE_FEATURE = `Feature: Example
|
|
66
66
|
Scenario: Homepage loads
|
|
67
67
|
Given I'm on the homepage
|
|
68
|
-
Then The page contains
|
|
68
|
+
Then The page contains heading "Welcome"
|
|
69
69
|
`;
|
|
70
70
|
function installCucumber(env) {
|
|
71
71
|
execPm(env, {
|
package/dist/bin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/init.ts","../src/detect.ts","../src/setup/cli.ts","../src/setup/cucumber.ts","../src/setup/github-actions.ts","../src/setup/playwright.ts","../src/bin.ts"],"sourcesContent":["import { confirm, intro, log, note, outro, spinner } from '@clack/prompts';\nimport { detectEnvironment, type Environment } from './detect.js';\nimport { installCli, isCliInstalled } from './setup/cli.js';\nimport { installCucumber, setupCucumber } from './setup/cucumber.js';\nimport { installGithubAction } from './setup/github-actions.js';\nimport { hasPlaywrightBrowsers, installPlaywrightBrowsers } from './setup/playwright.js';\n\nconst BDD_IMPORT = '@letsrunit/bdd/define';\n\nexport interface InitOptions {\n yes?: boolean;\n}\n\nasync function stepInstallCli(env: Environment): Promise<void> {\n if (isCliInstalled(env)) {\n log.success('@letsrunit/cli already installed');\n return;\n }\n\n const s = spinner();\n s.start('Installing @letsrunit/cli…');\n installCli(env);\n s.stop('@letsrunit/cli installed');\n}\n\nasync function stepEnsureCucumber(env: Environment, { yes }: InitOptions): Promise<boolean> {\n if (env.hasCucumber) return true;\n\n if (!yes && !env.isInteractive) {\n log.warn('@cucumber/cucumber not found. Install it to use letsrunit with Cucumber:');\n note('npm install --save-dev @cucumber/cucumber\\nThen run: npx letsrunit init', 'Setup Cucumber');\n return false;\n }\n\n if (!yes) {\n const install = await confirm({ message: '@cucumber/cucumber not found. Install it now?' });\n if (install !== true) return false;\n }\n\n const s = spinner();\n s.start('Installing @cucumber/cucumber…');\n installCucumber(env);\n s.stop('@cucumber/cucumber installed');\n return true;\n}\n\nfunction stepSetupCucumber(env: Environment): void {\n const result = setupCucumber(env);\n\n if (result.bddInstalled) log.success('@letsrunit/bdd installed');\n\n if (result.configResult === 'created') {\n log.success('features/support/world.js created');\n } else if (result.configResult === 'needs-manual-update') {\n log.warn('features/support/world.js exists but does not import @letsrunit/bdd.');\n note(`Add \"import '${BDD_IMPORT}';\" to features/support/world.js`, 'Action required');\n }\n\n if (result.featuresCreated) log.success('features/ directory created with example.feature');\n}\n\nasync function stepCheckPlaywrightBrowsers(env: Environment, { yes }: InitOptions): Promise<void> {\n if (hasPlaywrightBrowsers(env)) return;\n\n if (!yes && !env.isInteractive) {\n log.warn('Playwright Chromium browser not found.');\n note('npx playwright install chromium', 'Run to install browsers');\n return;\n }\n\n if (!yes) {\n const install = await confirm({ message: 'Playwright Chromium browser not found. Install it now?' });\n if (install !== true) return;\n }\n\n const s = spinner();\n s.start('Installing Playwright Chromium…');\n installPlaywrightBrowsers(env);\n s.stop('Playwright Chromium installed');\n}\n\nasync function stepAddGithubAction(env: Environment, { yes }: InitOptions): Promise<void> {\n if (!yes && !env.isInteractive) return;\n\n if (!yes) {\n const addAction = await confirm({ message: 'Add a GitHub Action to run features on push?' });\n if (addAction !== true) return;\n }\n\n const result = installGithubAction(env);\n if (result === 'created') {\n log.success('.github/workflows/letsrunit.yml created');\n } else {\n log.info('.github/workflows/letsrunit.yml already exists, skipped');\n }\n}\n\nexport async function init(options: InitOptions = {}): Promise<void> {\n intro('letsrunit init');\n\n const env = detectEnvironment();\n\n await stepInstallCli(env);\n\n const hasCucumber = await stepEnsureCucumber(env, options);\n if (hasCucumber) {\n stepSetupCucumber(env);\n await stepCheckPlaywrightBrowsers(env, options);\n await stepAddGithubAction(env, options);\n }\n\n outro('All done! Run npx letsrunit --help to get started.');\n}\n","import { execSync } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { join } from 'node:path';\n\nexport type PackageManager = 'yarn' | 'pnpm' | 'bun' | 'npm';\n\nexport interface Environment {\n isInteractive: boolean;\n packageManager: PackageManager;\n nodeVersion: number;\n hasCucumber: boolean;\n cwd: string;\n}\n\nexport interface PackageManagerArgs {\n npm: string;\n yarn: string;\n pnpm: string;\n bun: string;\n}\n\nexport function detectEnvironment(): Environment {\n const cwd = process.cwd();\n const isInteractive = Boolean(process.stdout.isTTY && process.stdin.isTTY);\n\n let packageManager: PackageManager = 'npm';\n if (existsSync(join(cwd, 'yarn.lock'))) packageManager = 'yarn';\n else if (existsSync(join(cwd, 'pnpm-lock.yaml'))) packageManager = 'pnpm';\n else if (existsSync(join(cwd, 'bun.lockb'))) packageManager = 'bun';\n\n const nodeVersion = parseInt(process.version.slice(1), 10);\n const hasCucumber = existsSync(join(cwd, 'node_modules', '@cucumber', 'cucumber', 'package.json'));\n\n return { isInteractive, packageManager, nodeVersion, hasCucumber, cwd };\n}\n\nfunction pmCmd(pm: string, args: PackageManagerArgs): string {\n if (pm === 'yarn') return `yarn ${args.yarn}`;\n if (pm === 'pnpm') return `pnpm ${args.pnpm}`;\n if (pm === 'bun') return `bun ${args.bun}`;\n return `npm ${args.npm}`;\n}\n\nexport function execPm(env: Pick<Environment, 'packageManager' | 'cwd'>, args: PackageManagerArgs) {\n const cmd = pmCmd(env.packageManager, args);\n return execSync(cmd, { stdio: 'inherit', cwd: env.cwd });\n}\n","import { existsSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { type Environment, execPm } from '../detect.js';\n\nexport function isCliInstalled({ cwd }: Pick<Environment, 'cwd'>): boolean {\n const pkgPath = join(cwd, 'package.json');\n if (!existsSync(pkgPath)) return false;\n\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8')) as {\n devDependencies?: Record<string, string>;\n dependencies?: Record<string, string>;\n };\n\n return '@letsrunit/cli' in (pkg.devDependencies ?? {}) || '@letsrunit/cli' in (pkg.dependencies ?? {});\n}\n\nexport function installCli(env: Pick<Environment, 'packageManager' | 'cwd'>): void {\n execPm(env, {\n npm: 'install --save-dev @letsrunit/cli',\n yarn: 'add --dev @letsrunit/cli',\n pnpm: 'add -D @letsrunit/cli',\n bun: 'add -d @letsrunit/cli',\n });\n}\n","import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { type Environment, execPm } from '../detect.js';\n\nconst BDD_IMPORT = '@letsrunit/bdd/define';\n\nconst CUCUMBER_CONFIG = `export default {\n worldParameters: {\n baseURL: 'http://localhost:3000',\n },\n};\n`;\n\nconst SUPPORT_FILE = `import { setDefaultTimeout } from '@cucumber/cucumber';\nimport '${BDD_IMPORT}';\n\nsetDefaultTimeout(30_000);\n`;\n\nconst EXAMPLE_FEATURE = `Feature: Example\n Scenario: Homepage loads\n Given I'm on the homepage\n Then The page contains text \"Welcome\"\n`;\n\nexport type CucumberConfigResult = 'created' | 'skipped' | 'needs-manual-update';\n\nexport interface CucumberSetupResult {\n bddInstalled: boolean;\n configResult: CucumberConfigResult;\n featuresCreated: boolean;\n}\n\nexport function installCucumber(env: Pick<Environment, 'packageManager' | 'cwd'>): void {\n execPm(env, {\n npm: 'install --save-dev @cucumber/cucumber',\n yarn: 'add --dev @cucumber/cucumber',\n pnpm: 'add -D @cucumber/cucumber',\n bun: 'add -d @cucumber/cucumber',\n });\n}\n\nfunction installBdd(env: Pick<Environment, 'packageManager' | 'cwd'>): boolean {\n const pkgPath = join(env.cwd, 'package.json');\n if (!existsSync(pkgPath)) return false;\n\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8')) as {\n devDependencies?: Record<string, string>;\n dependencies?: Record<string, string>;\n };\n\n const alreadyInstalled =\n '@letsrunit/bdd' in (pkg.devDependencies ?? {}) || '@letsrunit/bdd' in (pkg.dependencies ?? {});\n\n if (alreadyInstalled) return false;\n\n execPm(env, {\n npm: 'install --save-dev @letsrunit/bdd',\n yarn: 'add --dev @letsrunit/bdd',\n pnpm: 'add -D @letsrunit/bdd',\n bun: 'add -d @letsrunit/bdd',\n });\n\n return true;\n}\n\nfunction setupCucumberConfig({ cwd }: Pick<Environment, 'cwd'>): CucumberConfigResult {\n const supportDir = join(cwd, 'features', 'support');\n const supportPath = join(supportDir, 'world.js');\n\n if (existsSync(supportPath)) {\n const content = readFileSync(supportPath, 'utf-8');\n if (content.includes(BDD_IMPORT)) return 'skipped';\n return 'needs-manual-update';\n }\n\n const configPath = join(cwd, 'cucumber.js');\n if (!existsSync(configPath)) {\n writeFileSync(configPath, CUCUMBER_CONFIG, 'utf-8');\n }\n\n mkdirSync(supportDir, { recursive: true });\n writeFileSync(supportPath, SUPPORT_FILE, 'utf-8');\n return 'created';\n}\n\nfunction setupFeaturesDir({ cwd }: Pick<Environment, 'cwd'>): boolean {\n if (existsSync(join(cwd, 'features'))) {\n try {\n const hasFeatureFiles = readdirSync(join(cwd, 'features')).some((f) => f.endsWith('.feature'));\n if (hasFeatureFiles) return false;\n } catch {\n return false;\n }\n }\n\n try {\n const hasFeatureAtRoot = readdirSync(cwd).some((f) => f.endsWith('.feature'));\n if (hasFeatureAtRoot) return false;\n } catch {\n return false;\n }\n\n mkdirSync(join(cwd, 'features'), { recursive: true });\n writeFileSync(join(cwd, 'features', 'example.feature'), EXAMPLE_FEATURE, 'utf-8');\n return true;\n}\n\nexport function setupCucumber(env: Pick<Environment, 'packageManager' | 'cwd'>): CucumberSetupResult {\n const bddInstalled = installBdd(env);\n const configResult = setupCucumberConfig(env);\n const featuresCreated = setupFeaturesDir(env);\n return { bddInstalled, configResult, featuresCreated };\n}\n","import { existsSync, mkdirSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { Environment } from '../detect.js';\n\nexport type GithubActionsResult = 'created' | 'skipped';\n\nfunction setupStepsFor({ packageManager, nodeVersion }: Pick<Environment, 'packageManager' | 'nodeVersion'>): string {\n if (packageManager === 'yarn') return `\\\n - name: Enable Corepack\n run: corepack enable\n - uses: actions/setup-node@v4\n with:\n node-version: ${nodeVersion}\n cache: yarn\n - name: Install dependencies\n run: yarn install --immutable`;\n if (packageManager === 'pnpm') return `\\\n - uses: pnpm/action-setup@v4\n - uses: actions/setup-node@v4\n with:\n node-version: ${nodeVersion}\n cache: pnpm\n - name: Install dependencies\n run: pnpm install --frozen-lockfile`;\n if (packageManager === 'bun') return `\\\n - uses: oven-sh/setup-bun@v2\n - name: Install dependencies\n run: bun install --frozen-lockfile`;\n return `\\\n - uses: actions/setup-node@v4\n with:\n node-version: ${nodeVersion}\n cache: npm\n - name: Install dependencies\n run: npm ci`;\n}\n\nfunction workflowYaml(env: Pick<Environment, 'packageManager' | 'nodeVersion'>): string {\n const setupSteps = setupStepsFor(env);\n\n return `name: Features\non:\n push:\n branches: [main]\n pull_request:\n branches: [main]\njobs:\n features:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v4\n${setupSteps}\n - name: Install Playwright browsers\n run: npx playwright install chromium --with-deps\n - name: Run features\n run: npx cucumber-js\n`;\n}\n\nexport function installGithubAction(env: Pick<Environment, 'packageManager' | 'nodeVersion' | 'cwd'>): GithubActionsResult {\n const workflowDir = join(env.cwd, '.github', 'workflows');\n const workflowPath = join(workflowDir, 'letsrunit.yml');\n\n if (existsSync(workflowPath)) return 'skipped';\n\n mkdirSync(workflowDir, { recursive: true });\n writeFileSync(workflowPath, workflowYaml(env), 'utf-8');\n return 'created';\n}\n","import { execSync } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { type Environment, execPm } from '../detect.js';\n\nexport function hasPlaywrightBrowsers({ cwd }: Pick<Environment, 'cwd'>): boolean {\n if (!existsSync(join(cwd, 'node_modules', 'playwright-core', 'package.json'))) {\n return false;\n }\n try {\n const execPath = execSync(\n `node -e \"console.log(require('playwright-core').chromium.executablePath())\"`,\n { cwd, stdio: 'pipe', encoding: 'utf-8' },\n ).trim();\n return existsSync(execPath);\n } catch {\n return false;\n }\n}\n\nexport function installPlaywrightBrowsers(env: Pick<Environment, 'packageManager' | 'cwd'>): void {\n execPm(env, {\n npm: 'exec playwright install chromium',\n yarn: 'exec playwright install chromium',\n pnpm: 'exec playwright install chromium',\n bun: 'x playwright install chromium',\n });\n}\n","import { init } from './init.js';\n\nconst args = process.argv.slice(2);\nconst command = args.find((a) => !a.startsWith('-')) ?? 'init';\nconst yes = args.includes('--yes') || args.includes('-y');\n\nif (command === 'init') {\n init({ yes }).catch((err: unknown) => {\n console.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n });\n} else {\n console.error(`Unknown command: ${command}`);\n console.error('Usage: letsrunit init [--yes]');\n process.exit(1);\n}\n"],"mappings":"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","names":["args","existsSync","join","join","existsSync","existsSync","readFileSync","join","join","existsSync","readFileSync","existsSync","mkdirSync","writeFileSync","join","execSync","existsSync","join","existsSync","join","execSync","BDD_IMPORT","yes"]}
|
|
1
|
+
{"version":3,"sources":["../src/init.ts","../src/detect.ts","../src/setup/cli.ts","../src/setup/cucumber.ts","../src/setup/github-actions.ts","../src/setup/playwright.ts","../src/bin.ts"],"sourcesContent":["import { confirm, intro, log, note, outro, spinner } from '@clack/prompts';\nimport { detectEnvironment, type Environment } from './detect.js';\nimport { installCli, isCliInstalled } from './setup/cli.js';\nimport { installCucumber, setupCucumber } from './setup/cucumber.js';\nimport { installGithubAction } from './setup/github-actions.js';\nimport { hasPlaywrightBrowsers, installPlaywrightBrowsers } from './setup/playwright.js';\n\nconst BDD_IMPORT = '@letsrunit/bdd/define';\n\nexport interface InitOptions {\n yes?: boolean;\n}\n\nasync function stepInstallCli(env: Environment): Promise<void> {\n if (isCliInstalled(env)) {\n log.success('@letsrunit/cli already installed');\n return;\n }\n\n const s = spinner();\n s.start('Installing @letsrunit/cli…');\n installCli(env);\n s.stop('@letsrunit/cli installed');\n}\n\nasync function stepEnsureCucumber(env: Environment, { yes }: InitOptions): Promise<boolean> {\n if (env.hasCucumber) return true;\n\n if (!yes && !env.isInteractive) {\n log.warn('@cucumber/cucumber not found. Install it to use letsrunit with Cucumber:');\n note('npm install --save-dev @cucumber/cucumber\\nThen run: npx letsrunit init', 'Setup Cucumber');\n return false;\n }\n\n if (!yes) {\n const install = await confirm({ message: '@cucumber/cucumber not found. Install it now?' });\n if (install !== true) return false;\n }\n\n const s = spinner();\n s.start('Installing @cucumber/cucumber…');\n installCucumber(env);\n s.stop('@cucumber/cucumber installed');\n return true;\n}\n\nfunction stepSetupCucumber(env: Environment): void {\n const result = setupCucumber(env);\n\n if (result.bddInstalled) log.success('@letsrunit/bdd installed');\n\n if (result.configResult === 'created') {\n log.success('features/support/world.js created');\n } else if (result.configResult === 'needs-manual-update') {\n log.warn('features/support/world.js exists but does not import @letsrunit/bdd.');\n note(`Add \"import '${BDD_IMPORT}';\" to features/support/world.js`, 'Action required');\n }\n\n if (result.featuresCreated) log.success('features/ directory created with example.feature');\n}\n\nasync function stepCheckPlaywrightBrowsers(env: Environment, { yes }: InitOptions): Promise<void> {\n if (hasPlaywrightBrowsers(env)) return;\n\n if (!yes && !env.isInteractive) {\n log.warn('Playwright Chromium browser not found.');\n note('npx playwright install chromium', 'Run to install browsers');\n return;\n }\n\n if (!yes) {\n const install = await confirm({ message: 'Playwright Chromium browser not found. Install it now?' });\n if (install !== true) return;\n }\n\n const s = spinner();\n s.start('Installing Playwright Chromium…');\n installPlaywrightBrowsers(env);\n s.stop('Playwright Chromium installed');\n}\n\nasync function stepAddGithubAction(env: Environment, { yes }: InitOptions): Promise<void> {\n if (!yes && !env.isInteractive) return;\n\n if (!yes) {\n const addAction = await confirm({ message: 'Add a GitHub Action to run features on push?' });\n if (addAction !== true) return;\n }\n\n const result = installGithubAction(env);\n if (result === 'created') {\n log.success('.github/workflows/letsrunit.yml created');\n } else {\n log.info('.github/workflows/letsrunit.yml already exists, skipped');\n }\n}\n\nexport async function init(options: InitOptions = {}): Promise<void> {\n intro('letsrunit init');\n\n const env = detectEnvironment();\n\n await stepInstallCli(env);\n\n const hasCucumber = await stepEnsureCucumber(env, options);\n if (hasCucumber) {\n stepSetupCucumber(env);\n await stepCheckPlaywrightBrowsers(env, options);\n await stepAddGithubAction(env, options);\n }\n\n outro('All done! Run npx letsrunit --help to get started.');\n}\n","import { execSync } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { join } from 'node:path';\n\nexport type PackageManager = 'yarn' | 'pnpm' | 'bun' | 'npm';\n\nexport interface Environment {\n isInteractive: boolean;\n packageManager: PackageManager;\n nodeVersion: number;\n hasCucumber: boolean;\n cwd: string;\n}\n\nexport interface PackageManagerArgs {\n npm: string;\n yarn: string;\n pnpm: string;\n bun: string;\n}\n\nexport function detectEnvironment(): Environment {\n const cwd = process.cwd();\n const isInteractive = Boolean(process.stdout.isTTY && process.stdin.isTTY);\n\n let packageManager: PackageManager = 'npm';\n if (existsSync(join(cwd, 'yarn.lock'))) packageManager = 'yarn';\n else if (existsSync(join(cwd, 'pnpm-lock.yaml'))) packageManager = 'pnpm';\n else if (existsSync(join(cwd, 'bun.lockb')) || existsSync(join(cwd, 'bun.lock'))) packageManager = 'bun';\n\n const nodeVersion = parseInt(process.version.slice(1), 10);\n const hasCucumber = existsSync(join(cwd, 'node_modules', '@cucumber', 'cucumber', 'package.json'));\n\n return { isInteractive, packageManager, nodeVersion, hasCucumber, cwd };\n}\n\nfunction pmCmd(pm: string, args: PackageManagerArgs): string {\n if (pm === 'yarn') return `yarn ${args.yarn}`;\n if (pm === 'pnpm') return `pnpm ${args.pnpm}`;\n if (pm === 'bun') return `bun ${args.bun}`;\n return `npm ${args.npm}`;\n}\n\nexport function execPm(env: Pick<Environment, 'packageManager' | 'cwd'>, args: PackageManagerArgs) {\n const cmd = pmCmd(env.packageManager, args);\n return execSync(cmd, { stdio: 'inherit', cwd: env.cwd });\n}\n","import { existsSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { type Environment, execPm } from '../detect.js';\n\nexport function isCliInstalled({ cwd }: Pick<Environment, 'cwd'>): boolean {\n const pkgPath = join(cwd, 'package.json');\n if (!existsSync(pkgPath)) return false;\n\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8')) as {\n devDependencies?: Record<string, string>;\n dependencies?: Record<string, string>;\n };\n\n return '@letsrunit/cli' in (pkg.devDependencies ?? {}) || '@letsrunit/cli' in (pkg.dependencies ?? {});\n}\n\nexport function installCli(env: Pick<Environment, 'packageManager' | 'cwd'>): void {\n execPm(env, {\n npm: 'install --save-dev @letsrunit/cli',\n yarn: 'add --dev @letsrunit/cli',\n pnpm: 'add -D @letsrunit/cli',\n bun: 'add -d @letsrunit/cli',\n });\n}\n","import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { type Environment, execPm } from '../detect.js';\n\nconst BDD_IMPORT = '@letsrunit/bdd/define';\n\nconst CUCUMBER_CONFIG = `export default {\n worldParameters: {\n baseURL: 'http://localhost:3000',\n },\n};\n`;\n\nconst SUPPORT_FILE = `import { setDefaultTimeout } from '@cucumber/cucumber';\nimport '${BDD_IMPORT}';\n\nsetDefaultTimeout(30_000);\n`;\n\nconst EXAMPLE_FEATURE = `Feature: Example\n Scenario: Homepage loads\n Given I'm on the homepage\n Then The page contains heading \"Welcome\"\n`;\n\nexport type CucumberConfigResult = 'created' | 'skipped' | 'needs-manual-update';\n\nexport interface CucumberSetupResult {\n bddInstalled: boolean;\n configResult: CucumberConfigResult;\n featuresCreated: boolean;\n}\n\nexport function installCucumber(env: Pick<Environment, 'packageManager' | 'cwd'>): void {\n execPm(env, {\n npm: 'install --save-dev @cucumber/cucumber',\n yarn: 'add --dev @cucumber/cucumber',\n pnpm: 'add -D @cucumber/cucumber',\n bun: 'add -d @cucumber/cucumber',\n });\n}\n\nfunction installBdd(env: Pick<Environment, 'packageManager' | 'cwd'>): boolean {\n const pkgPath = join(env.cwd, 'package.json');\n if (!existsSync(pkgPath)) return false;\n\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8')) as {\n devDependencies?: Record<string, string>;\n dependencies?: Record<string, string>;\n };\n\n const alreadyInstalled =\n '@letsrunit/bdd' in (pkg.devDependencies ?? {}) || '@letsrunit/bdd' in (pkg.dependencies ?? {});\n\n if (alreadyInstalled) return false;\n\n execPm(env, {\n npm: 'install --save-dev @letsrunit/bdd',\n yarn: 'add --dev @letsrunit/bdd',\n pnpm: 'add -D @letsrunit/bdd',\n bun: 'add -d @letsrunit/bdd',\n });\n\n return true;\n}\n\nfunction setupCucumberConfig({ cwd }: Pick<Environment, 'cwd'>): CucumberConfigResult {\n const supportDir = join(cwd, 'features', 'support');\n const supportPath = join(supportDir, 'world.js');\n\n if (existsSync(supportPath)) {\n const content = readFileSync(supportPath, 'utf-8');\n if (content.includes(BDD_IMPORT)) return 'skipped';\n return 'needs-manual-update';\n }\n\n const configPath = join(cwd, 'cucumber.js');\n if (!existsSync(configPath)) {\n writeFileSync(configPath, CUCUMBER_CONFIG, 'utf-8');\n }\n\n mkdirSync(supportDir, { recursive: true });\n writeFileSync(supportPath, SUPPORT_FILE, 'utf-8');\n return 'created';\n}\n\nfunction setupFeaturesDir({ cwd }: Pick<Environment, 'cwd'>): boolean {\n if (existsSync(join(cwd, 'features'))) {\n try {\n const hasFeatureFiles = readdirSync(join(cwd, 'features')).some((f) => f.endsWith('.feature'));\n if (hasFeatureFiles) return false;\n } catch {\n return false;\n }\n }\n\n try {\n const hasFeatureAtRoot = readdirSync(cwd).some((f) => f.endsWith('.feature'));\n if (hasFeatureAtRoot) return false;\n } catch {\n return false;\n }\n\n mkdirSync(join(cwd, 'features'), { recursive: true });\n writeFileSync(join(cwd, 'features', 'example.feature'), EXAMPLE_FEATURE, 'utf-8');\n return true;\n}\n\nexport function setupCucumber(env: Pick<Environment, 'packageManager' | 'cwd'>): CucumberSetupResult {\n const bddInstalled = installBdd(env);\n const configResult = setupCucumberConfig(env);\n const featuresCreated = setupFeaturesDir(env);\n return { bddInstalled, configResult, featuresCreated };\n}\n","import { existsSync, mkdirSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { Environment } from '../detect.js';\n\nexport type GithubActionsResult = 'created' | 'skipped';\n\nfunction setupStepsFor({ packageManager, nodeVersion }: Pick<Environment, 'packageManager' | 'nodeVersion'>): string {\n if (packageManager === 'yarn') return `\\\n - name: Enable Corepack\n run: corepack enable\n - uses: actions/setup-node@v4\n with:\n node-version: ${nodeVersion}\n cache: yarn\n - name: Install dependencies\n run: yarn install --immutable`;\n if (packageManager === 'pnpm') return `\\\n - uses: pnpm/action-setup@v4\n - uses: actions/setup-node@v4\n with:\n node-version: ${nodeVersion}\n cache: pnpm\n - name: Install dependencies\n run: pnpm install --frozen-lockfile`;\n if (packageManager === 'bun') return `\\\n - uses: oven-sh/setup-bun@v2\n - name: Install dependencies\n run: bun install --frozen-lockfile`;\n return `\\\n - uses: actions/setup-node@v4\n with:\n node-version: ${nodeVersion}\n cache: npm\n - name: Install dependencies\n run: npm ci`;\n}\n\nfunction workflowYaml(env: Pick<Environment, 'packageManager' | 'nodeVersion'>): string {\n const setupSteps = setupStepsFor(env);\n\n return `name: Features\non:\n push:\n branches: [main]\n pull_request:\n branches: [main]\njobs:\n features:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v4\n${setupSteps}\n - name: Install Playwright browsers\n run: npx playwright install chromium --with-deps\n - name: Run features\n run: npx cucumber-js\n`;\n}\n\nexport function installGithubAction(env: Pick<Environment, 'packageManager' | 'nodeVersion' | 'cwd'>): GithubActionsResult {\n const workflowDir = join(env.cwd, '.github', 'workflows');\n const workflowPath = join(workflowDir, 'letsrunit.yml');\n\n if (existsSync(workflowPath)) return 'skipped';\n\n mkdirSync(workflowDir, { recursive: true });\n writeFileSync(workflowPath, workflowYaml(env), 'utf-8');\n return 'created';\n}\n","import { execSync } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { type Environment, execPm } from '../detect.js';\n\nexport function hasPlaywrightBrowsers({ cwd }: Pick<Environment, 'cwd'>): boolean {\n if (!existsSync(join(cwd, 'node_modules', 'playwright-core', 'package.json'))) {\n return false;\n }\n try {\n const execPath = execSync(\n `node -e \"console.log(require('playwright-core').chromium.executablePath())\"`,\n { cwd, stdio: 'pipe', encoding: 'utf-8' },\n ).trim();\n return existsSync(execPath);\n } catch {\n return false;\n }\n}\n\nexport function installPlaywrightBrowsers(env: Pick<Environment, 'packageManager' | 'cwd'>): void {\n execPm(env, {\n npm: 'exec playwright install chromium',\n yarn: 'exec playwright install chromium',\n pnpm: 'exec playwright install chromium',\n bun: 'x playwright install chromium',\n });\n}\n","import { init } from './init.js';\n\nconst args = process.argv.slice(2);\nconst command = args.find((a) => !a.startsWith('-')) ?? 'init';\nconst yes = args.includes('--yes') || args.includes('-y');\n\nif (command === 'init') {\n init({ yes }).catch((err: unknown) => {\n console.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n });\n} else {\n console.error(`Unknown command: ${command}`);\n console.error('Usage: letsrunit init [--yes]');\n process.exit(1);\n}\n"],"mappings":"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","names":["args","existsSync","join","join","existsSync","existsSync","readFileSync","join","join","existsSync","readFileSync","existsSync","mkdirSync","writeFileSync","join","execSync","existsSync","join","existsSync","join","execSync","BDD_IMPORT","yes"]}
|
package/dist/index.js
CHANGED
|
@@ -11,7 +11,7 @@ function detectEnvironment() {
|
|
|
11
11
|
let packageManager = "npm";
|
|
12
12
|
if (existsSync(join(cwd, "yarn.lock"))) packageManager = "yarn";
|
|
13
13
|
else if (existsSync(join(cwd, "pnpm-lock.yaml"))) packageManager = "pnpm";
|
|
14
|
-
else if (existsSync(join(cwd, "bun.lockb"))) packageManager = "bun";
|
|
14
|
+
else if (existsSync(join(cwd, "bun.lockb")) || existsSync(join(cwd, "bun.lock"))) packageManager = "bun";
|
|
15
15
|
const nodeVersion = parseInt(process.version.slice(1), 10);
|
|
16
16
|
const hasCucumber = existsSync(join(cwd, "node_modules", "@cucumber", "cucumber", "package.json"));
|
|
17
17
|
return { isInteractive, packageManager, nodeVersion, hasCucumber, cwd };
|
|
@@ -63,7 +63,7 @@ setDefaultTimeout(30_000);
|
|
|
63
63
|
var EXAMPLE_FEATURE = `Feature: Example
|
|
64
64
|
Scenario: Homepage loads
|
|
65
65
|
Given I'm on the homepage
|
|
66
|
-
Then The page contains
|
|
66
|
+
Then The page contains heading "Welcome"
|
|
67
67
|
`;
|
|
68
68
|
function installCucumber(env) {
|
|
69
69
|
execPm(env, {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/init.ts","../src/detect.ts","../src/setup/cli.ts","../src/setup/cucumber.ts","../src/setup/github-actions.ts","../src/setup/playwright.ts"],"sourcesContent":["import { confirm, intro, log, note, outro, spinner } from '@clack/prompts';\nimport { detectEnvironment, type Environment } from './detect.js';\nimport { installCli, isCliInstalled } from './setup/cli.js';\nimport { installCucumber, setupCucumber } from './setup/cucumber.js';\nimport { installGithubAction } from './setup/github-actions.js';\nimport { hasPlaywrightBrowsers, installPlaywrightBrowsers } from './setup/playwright.js';\n\nconst BDD_IMPORT = '@letsrunit/bdd/define';\n\nexport interface InitOptions {\n yes?: boolean;\n}\n\nasync function stepInstallCli(env: Environment): Promise<void> {\n if (isCliInstalled(env)) {\n log.success('@letsrunit/cli already installed');\n return;\n }\n\n const s = spinner();\n s.start('Installing @letsrunit/cli…');\n installCli(env);\n s.stop('@letsrunit/cli installed');\n}\n\nasync function stepEnsureCucumber(env: Environment, { yes }: InitOptions): Promise<boolean> {\n if (env.hasCucumber) return true;\n\n if (!yes && !env.isInteractive) {\n log.warn('@cucumber/cucumber not found. Install it to use letsrunit with Cucumber:');\n note('npm install --save-dev @cucumber/cucumber\\nThen run: npx letsrunit init', 'Setup Cucumber');\n return false;\n }\n\n if (!yes) {\n const install = await confirm({ message: '@cucumber/cucumber not found. Install it now?' });\n if (install !== true) return false;\n }\n\n const s = spinner();\n s.start('Installing @cucumber/cucumber…');\n installCucumber(env);\n s.stop('@cucumber/cucumber installed');\n return true;\n}\n\nfunction stepSetupCucumber(env: Environment): void {\n const result = setupCucumber(env);\n\n if (result.bddInstalled) log.success('@letsrunit/bdd installed');\n\n if (result.configResult === 'created') {\n log.success('features/support/world.js created');\n } else if (result.configResult === 'needs-manual-update') {\n log.warn('features/support/world.js exists but does not import @letsrunit/bdd.');\n note(`Add \"import '${BDD_IMPORT}';\" to features/support/world.js`, 'Action required');\n }\n\n if (result.featuresCreated) log.success('features/ directory created with example.feature');\n}\n\nasync function stepCheckPlaywrightBrowsers(env: Environment, { yes }: InitOptions): Promise<void> {\n if (hasPlaywrightBrowsers(env)) return;\n\n if (!yes && !env.isInteractive) {\n log.warn('Playwright Chromium browser not found.');\n note('npx playwright install chromium', 'Run to install browsers');\n return;\n }\n\n if (!yes) {\n const install = await confirm({ message: 'Playwright Chromium browser not found. Install it now?' });\n if (install !== true) return;\n }\n\n const s = spinner();\n s.start('Installing Playwright Chromium…');\n installPlaywrightBrowsers(env);\n s.stop('Playwright Chromium installed');\n}\n\nasync function stepAddGithubAction(env: Environment, { yes }: InitOptions): Promise<void> {\n if (!yes && !env.isInteractive) return;\n\n if (!yes) {\n const addAction = await confirm({ message: 'Add a GitHub Action to run features on push?' });\n if (addAction !== true) return;\n }\n\n const result = installGithubAction(env);\n if (result === 'created') {\n log.success('.github/workflows/letsrunit.yml created');\n } else {\n log.info('.github/workflows/letsrunit.yml already exists, skipped');\n }\n}\n\nexport async function init(options: InitOptions = {}): Promise<void> {\n intro('letsrunit init');\n\n const env = detectEnvironment();\n\n await stepInstallCli(env);\n\n const hasCucumber = await stepEnsureCucumber(env, options);\n if (hasCucumber) {\n stepSetupCucumber(env);\n await stepCheckPlaywrightBrowsers(env, options);\n await stepAddGithubAction(env, options);\n }\n\n outro('All done! Run npx letsrunit --help to get started.');\n}\n","import { execSync } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { join } from 'node:path';\n\nexport type PackageManager = 'yarn' | 'pnpm' | 'bun' | 'npm';\n\nexport interface Environment {\n isInteractive: boolean;\n packageManager: PackageManager;\n nodeVersion: number;\n hasCucumber: boolean;\n cwd: string;\n}\n\nexport interface PackageManagerArgs {\n npm: string;\n yarn: string;\n pnpm: string;\n bun: string;\n}\n\nexport function detectEnvironment(): Environment {\n const cwd = process.cwd();\n const isInteractive = Boolean(process.stdout.isTTY && process.stdin.isTTY);\n\n let packageManager: PackageManager = 'npm';\n if (existsSync(join(cwd, 'yarn.lock'))) packageManager = 'yarn';\n else if (existsSync(join(cwd, 'pnpm-lock.yaml'))) packageManager = 'pnpm';\n else if (existsSync(join(cwd, 'bun.lockb'))) packageManager = 'bun';\n\n const nodeVersion = parseInt(process.version.slice(1), 10);\n const hasCucumber = existsSync(join(cwd, 'node_modules', '@cucumber', 'cucumber', 'package.json'));\n\n return { isInteractive, packageManager, nodeVersion, hasCucumber, cwd };\n}\n\nfunction pmCmd(pm: string, args: PackageManagerArgs): string {\n if (pm === 'yarn') return `yarn ${args.yarn}`;\n if (pm === 'pnpm') return `pnpm ${args.pnpm}`;\n if (pm === 'bun') return `bun ${args.bun}`;\n return `npm ${args.npm}`;\n}\n\nexport function execPm(env: Pick<Environment, 'packageManager' | 'cwd'>, args: PackageManagerArgs) {\n const cmd = pmCmd(env.packageManager, args);\n return execSync(cmd, { stdio: 'inherit', cwd: env.cwd });\n}\n","import { existsSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { type Environment, execPm } from '../detect.js';\n\nexport function isCliInstalled({ cwd }: Pick<Environment, 'cwd'>): boolean {\n const pkgPath = join(cwd, 'package.json');\n if (!existsSync(pkgPath)) return false;\n\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8')) as {\n devDependencies?: Record<string, string>;\n dependencies?: Record<string, string>;\n };\n\n return '@letsrunit/cli' in (pkg.devDependencies ?? {}) || '@letsrunit/cli' in (pkg.dependencies ?? {});\n}\n\nexport function installCli(env: Pick<Environment, 'packageManager' | 'cwd'>): void {\n execPm(env, {\n npm: 'install --save-dev @letsrunit/cli',\n yarn: 'add --dev @letsrunit/cli',\n pnpm: 'add -D @letsrunit/cli',\n bun: 'add -d @letsrunit/cli',\n });\n}\n","import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { type Environment, execPm } from '../detect.js';\n\nconst BDD_IMPORT = '@letsrunit/bdd/define';\n\nconst CUCUMBER_CONFIG = `export default {\n worldParameters: {\n baseURL: 'http://localhost:3000',\n },\n};\n`;\n\nconst SUPPORT_FILE = `import { setDefaultTimeout } from '@cucumber/cucumber';\nimport '${BDD_IMPORT}';\n\nsetDefaultTimeout(30_000);\n`;\n\nconst EXAMPLE_FEATURE = `Feature: Example\n Scenario: Homepage loads\n Given I'm on the homepage\n Then The page contains text \"Welcome\"\n`;\n\nexport type CucumberConfigResult = 'created' | 'skipped' | 'needs-manual-update';\n\nexport interface CucumberSetupResult {\n bddInstalled: boolean;\n configResult: CucumberConfigResult;\n featuresCreated: boolean;\n}\n\nexport function installCucumber(env: Pick<Environment, 'packageManager' | 'cwd'>): void {\n execPm(env, {\n npm: 'install --save-dev @cucumber/cucumber',\n yarn: 'add --dev @cucumber/cucumber',\n pnpm: 'add -D @cucumber/cucumber',\n bun: 'add -d @cucumber/cucumber',\n });\n}\n\nfunction installBdd(env: Pick<Environment, 'packageManager' | 'cwd'>): boolean {\n const pkgPath = join(env.cwd, 'package.json');\n if (!existsSync(pkgPath)) return false;\n\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8')) as {\n devDependencies?: Record<string, string>;\n dependencies?: Record<string, string>;\n };\n\n const alreadyInstalled =\n '@letsrunit/bdd' in (pkg.devDependencies ?? {}) || '@letsrunit/bdd' in (pkg.dependencies ?? {});\n\n if (alreadyInstalled) return false;\n\n execPm(env, {\n npm: 'install --save-dev @letsrunit/bdd',\n yarn: 'add --dev @letsrunit/bdd',\n pnpm: 'add -D @letsrunit/bdd',\n bun: 'add -d @letsrunit/bdd',\n });\n\n return true;\n}\n\nfunction setupCucumberConfig({ cwd }: Pick<Environment, 'cwd'>): CucumberConfigResult {\n const supportDir = join(cwd, 'features', 'support');\n const supportPath = join(supportDir, 'world.js');\n\n if (existsSync(supportPath)) {\n const content = readFileSync(supportPath, 'utf-8');\n if (content.includes(BDD_IMPORT)) return 'skipped';\n return 'needs-manual-update';\n }\n\n const configPath = join(cwd, 'cucumber.js');\n if (!existsSync(configPath)) {\n writeFileSync(configPath, CUCUMBER_CONFIG, 'utf-8');\n }\n\n mkdirSync(supportDir, { recursive: true });\n writeFileSync(supportPath, SUPPORT_FILE, 'utf-8');\n return 'created';\n}\n\nfunction setupFeaturesDir({ cwd }: Pick<Environment, 'cwd'>): boolean {\n if (existsSync(join(cwd, 'features'))) {\n try {\n const hasFeatureFiles = readdirSync(join(cwd, 'features')).some((f) => f.endsWith('.feature'));\n if (hasFeatureFiles) return false;\n } catch {\n return false;\n }\n }\n\n try {\n const hasFeatureAtRoot = readdirSync(cwd).some((f) => f.endsWith('.feature'));\n if (hasFeatureAtRoot) return false;\n } catch {\n return false;\n }\n\n mkdirSync(join(cwd, 'features'), { recursive: true });\n writeFileSync(join(cwd, 'features', 'example.feature'), EXAMPLE_FEATURE, 'utf-8');\n return true;\n}\n\nexport function setupCucumber(env: Pick<Environment, 'packageManager' | 'cwd'>): CucumberSetupResult {\n const bddInstalled = installBdd(env);\n const configResult = setupCucumberConfig(env);\n const featuresCreated = setupFeaturesDir(env);\n return { bddInstalled, configResult, featuresCreated };\n}\n","import { existsSync, mkdirSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { Environment } from '../detect.js';\n\nexport type GithubActionsResult = 'created' | 'skipped';\n\nfunction setupStepsFor({ packageManager, nodeVersion }: Pick<Environment, 'packageManager' | 'nodeVersion'>): string {\n if (packageManager === 'yarn') return `\\\n - name: Enable Corepack\n run: corepack enable\n - uses: actions/setup-node@v4\n with:\n node-version: ${nodeVersion}\n cache: yarn\n - name: Install dependencies\n run: yarn install --immutable`;\n if (packageManager === 'pnpm') return `\\\n - uses: pnpm/action-setup@v4\n - uses: actions/setup-node@v4\n with:\n node-version: ${nodeVersion}\n cache: pnpm\n - name: Install dependencies\n run: pnpm install --frozen-lockfile`;\n if (packageManager === 'bun') return `\\\n - uses: oven-sh/setup-bun@v2\n - name: Install dependencies\n run: bun install --frozen-lockfile`;\n return `\\\n - uses: actions/setup-node@v4\n with:\n node-version: ${nodeVersion}\n cache: npm\n - name: Install dependencies\n run: npm ci`;\n}\n\nfunction workflowYaml(env: Pick<Environment, 'packageManager' | 'nodeVersion'>): string {\n const setupSteps = setupStepsFor(env);\n\n return `name: Features\non:\n push:\n branches: [main]\n pull_request:\n branches: [main]\njobs:\n features:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v4\n${setupSteps}\n - name: Install Playwright browsers\n run: npx playwright install chromium --with-deps\n - name: Run features\n run: npx cucumber-js\n`;\n}\n\nexport function installGithubAction(env: Pick<Environment, 'packageManager' | 'nodeVersion' | 'cwd'>): GithubActionsResult {\n const workflowDir = join(env.cwd, '.github', 'workflows');\n const workflowPath = join(workflowDir, 'letsrunit.yml');\n\n if (existsSync(workflowPath)) return 'skipped';\n\n mkdirSync(workflowDir, { recursive: true });\n writeFileSync(workflowPath, workflowYaml(env), 'utf-8');\n return 'created';\n}\n","import { execSync } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { type Environment, execPm } from '../detect.js';\n\nexport function hasPlaywrightBrowsers({ cwd }: Pick<Environment, 'cwd'>): boolean {\n if (!existsSync(join(cwd, 'node_modules', 'playwright-core', 'package.json'))) {\n return false;\n }\n try {\n const execPath = execSync(\n `node -e \"console.log(require('playwright-core').chromium.executablePath())\"`,\n { cwd, stdio: 'pipe', encoding: 'utf-8' },\n ).trim();\n return existsSync(execPath);\n } catch {\n return false;\n }\n}\n\nexport function installPlaywrightBrowsers(env: Pick<Environment, 'packageManager' | 'cwd'>): void {\n execPm(env, {\n npm: 'exec playwright install chromium',\n yarn: 'exec playwright install chromium',\n pnpm: 'exec playwright install chromium',\n bun: 'x playwright install chromium',\n });\n}\n"],"mappings":"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","names":["existsSync","join","join","existsSync","existsSync","readFileSync","join","join","existsSync","readFileSync","existsSync","mkdirSync","writeFileSync","join","execSync","existsSync","join","existsSync","join","execSync","BDD_IMPORT"]}
|
|
1
|
+
{"version":3,"sources":["../src/init.ts","../src/detect.ts","../src/setup/cli.ts","../src/setup/cucumber.ts","../src/setup/github-actions.ts","../src/setup/playwright.ts"],"sourcesContent":["import { confirm, intro, log, note, outro, spinner } from '@clack/prompts';\nimport { detectEnvironment, type Environment } from './detect.js';\nimport { installCli, isCliInstalled } from './setup/cli.js';\nimport { installCucumber, setupCucumber } from './setup/cucumber.js';\nimport { installGithubAction } from './setup/github-actions.js';\nimport { hasPlaywrightBrowsers, installPlaywrightBrowsers } from './setup/playwright.js';\n\nconst BDD_IMPORT = '@letsrunit/bdd/define';\n\nexport interface InitOptions {\n yes?: boolean;\n}\n\nasync function stepInstallCli(env: Environment): Promise<void> {\n if (isCliInstalled(env)) {\n log.success('@letsrunit/cli already installed');\n return;\n }\n\n const s = spinner();\n s.start('Installing @letsrunit/cli…');\n installCli(env);\n s.stop('@letsrunit/cli installed');\n}\n\nasync function stepEnsureCucumber(env: Environment, { yes }: InitOptions): Promise<boolean> {\n if (env.hasCucumber) return true;\n\n if (!yes && !env.isInteractive) {\n log.warn('@cucumber/cucumber not found. Install it to use letsrunit with Cucumber:');\n note('npm install --save-dev @cucumber/cucumber\\nThen run: npx letsrunit init', 'Setup Cucumber');\n return false;\n }\n\n if (!yes) {\n const install = await confirm({ message: '@cucumber/cucumber not found. Install it now?' });\n if (install !== true) return false;\n }\n\n const s = spinner();\n s.start('Installing @cucumber/cucumber…');\n installCucumber(env);\n s.stop('@cucumber/cucumber installed');\n return true;\n}\n\nfunction stepSetupCucumber(env: Environment): void {\n const result = setupCucumber(env);\n\n if (result.bddInstalled) log.success('@letsrunit/bdd installed');\n\n if (result.configResult === 'created') {\n log.success('features/support/world.js created');\n } else if (result.configResult === 'needs-manual-update') {\n log.warn('features/support/world.js exists but does not import @letsrunit/bdd.');\n note(`Add \"import '${BDD_IMPORT}';\" to features/support/world.js`, 'Action required');\n }\n\n if (result.featuresCreated) log.success('features/ directory created with example.feature');\n}\n\nasync function stepCheckPlaywrightBrowsers(env: Environment, { yes }: InitOptions): Promise<void> {\n if (hasPlaywrightBrowsers(env)) return;\n\n if (!yes && !env.isInteractive) {\n log.warn('Playwright Chromium browser not found.');\n note('npx playwright install chromium', 'Run to install browsers');\n return;\n }\n\n if (!yes) {\n const install = await confirm({ message: 'Playwright Chromium browser not found. Install it now?' });\n if (install !== true) return;\n }\n\n const s = spinner();\n s.start('Installing Playwright Chromium…');\n installPlaywrightBrowsers(env);\n s.stop('Playwright Chromium installed');\n}\n\nasync function stepAddGithubAction(env: Environment, { yes }: InitOptions): Promise<void> {\n if (!yes && !env.isInteractive) return;\n\n if (!yes) {\n const addAction = await confirm({ message: 'Add a GitHub Action to run features on push?' });\n if (addAction !== true) return;\n }\n\n const result = installGithubAction(env);\n if (result === 'created') {\n log.success('.github/workflows/letsrunit.yml created');\n } else {\n log.info('.github/workflows/letsrunit.yml already exists, skipped');\n }\n}\n\nexport async function init(options: InitOptions = {}): Promise<void> {\n intro('letsrunit init');\n\n const env = detectEnvironment();\n\n await stepInstallCli(env);\n\n const hasCucumber = await stepEnsureCucumber(env, options);\n if (hasCucumber) {\n stepSetupCucumber(env);\n await stepCheckPlaywrightBrowsers(env, options);\n await stepAddGithubAction(env, options);\n }\n\n outro('All done! Run npx letsrunit --help to get started.');\n}\n","import { execSync } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { join } from 'node:path';\n\nexport type PackageManager = 'yarn' | 'pnpm' | 'bun' | 'npm';\n\nexport interface Environment {\n isInteractive: boolean;\n packageManager: PackageManager;\n nodeVersion: number;\n hasCucumber: boolean;\n cwd: string;\n}\n\nexport interface PackageManagerArgs {\n npm: string;\n yarn: string;\n pnpm: string;\n bun: string;\n}\n\nexport function detectEnvironment(): Environment {\n const cwd = process.cwd();\n const isInteractive = Boolean(process.stdout.isTTY && process.stdin.isTTY);\n\n let packageManager: PackageManager = 'npm';\n if (existsSync(join(cwd, 'yarn.lock'))) packageManager = 'yarn';\n else if (existsSync(join(cwd, 'pnpm-lock.yaml'))) packageManager = 'pnpm';\n else if (existsSync(join(cwd, 'bun.lockb')) || existsSync(join(cwd, 'bun.lock'))) packageManager = 'bun';\n\n const nodeVersion = parseInt(process.version.slice(1), 10);\n const hasCucumber = existsSync(join(cwd, 'node_modules', '@cucumber', 'cucumber', 'package.json'));\n\n return { isInteractive, packageManager, nodeVersion, hasCucumber, cwd };\n}\n\nfunction pmCmd(pm: string, args: PackageManagerArgs): string {\n if (pm === 'yarn') return `yarn ${args.yarn}`;\n if (pm === 'pnpm') return `pnpm ${args.pnpm}`;\n if (pm === 'bun') return `bun ${args.bun}`;\n return `npm ${args.npm}`;\n}\n\nexport function execPm(env: Pick<Environment, 'packageManager' | 'cwd'>, args: PackageManagerArgs) {\n const cmd = pmCmd(env.packageManager, args);\n return execSync(cmd, { stdio: 'inherit', cwd: env.cwd });\n}\n","import { existsSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { type Environment, execPm } from '../detect.js';\n\nexport function isCliInstalled({ cwd }: Pick<Environment, 'cwd'>): boolean {\n const pkgPath = join(cwd, 'package.json');\n if (!existsSync(pkgPath)) return false;\n\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8')) as {\n devDependencies?: Record<string, string>;\n dependencies?: Record<string, string>;\n };\n\n return '@letsrunit/cli' in (pkg.devDependencies ?? {}) || '@letsrunit/cli' in (pkg.dependencies ?? {});\n}\n\nexport function installCli(env: Pick<Environment, 'packageManager' | 'cwd'>): void {\n execPm(env, {\n npm: 'install --save-dev @letsrunit/cli',\n yarn: 'add --dev @letsrunit/cli',\n pnpm: 'add -D @letsrunit/cli',\n bun: 'add -d @letsrunit/cli',\n });\n}\n","import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { type Environment, execPm } from '../detect.js';\n\nconst BDD_IMPORT = '@letsrunit/bdd/define';\n\nconst CUCUMBER_CONFIG = `export default {\n worldParameters: {\n baseURL: 'http://localhost:3000',\n },\n};\n`;\n\nconst SUPPORT_FILE = `import { setDefaultTimeout } from '@cucumber/cucumber';\nimport '${BDD_IMPORT}';\n\nsetDefaultTimeout(30_000);\n`;\n\nconst EXAMPLE_FEATURE = `Feature: Example\n Scenario: Homepage loads\n Given I'm on the homepage\n Then The page contains heading \"Welcome\"\n`;\n\nexport type CucumberConfigResult = 'created' | 'skipped' | 'needs-manual-update';\n\nexport interface CucumberSetupResult {\n bddInstalled: boolean;\n configResult: CucumberConfigResult;\n featuresCreated: boolean;\n}\n\nexport function installCucumber(env: Pick<Environment, 'packageManager' | 'cwd'>): void {\n execPm(env, {\n npm: 'install --save-dev @cucumber/cucumber',\n yarn: 'add --dev @cucumber/cucumber',\n pnpm: 'add -D @cucumber/cucumber',\n bun: 'add -d @cucumber/cucumber',\n });\n}\n\nfunction installBdd(env: Pick<Environment, 'packageManager' | 'cwd'>): boolean {\n const pkgPath = join(env.cwd, 'package.json');\n if (!existsSync(pkgPath)) return false;\n\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8')) as {\n devDependencies?: Record<string, string>;\n dependencies?: Record<string, string>;\n };\n\n const alreadyInstalled =\n '@letsrunit/bdd' in (pkg.devDependencies ?? {}) || '@letsrunit/bdd' in (pkg.dependencies ?? {});\n\n if (alreadyInstalled) return false;\n\n execPm(env, {\n npm: 'install --save-dev @letsrunit/bdd',\n yarn: 'add --dev @letsrunit/bdd',\n pnpm: 'add -D @letsrunit/bdd',\n bun: 'add -d @letsrunit/bdd',\n });\n\n return true;\n}\n\nfunction setupCucumberConfig({ cwd }: Pick<Environment, 'cwd'>): CucumberConfigResult {\n const supportDir = join(cwd, 'features', 'support');\n const supportPath = join(supportDir, 'world.js');\n\n if (existsSync(supportPath)) {\n const content = readFileSync(supportPath, 'utf-8');\n if (content.includes(BDD_IMPORT)) return 'skipped';\n return 'needs-manual-update';\n }\n\n const configPath = join(cwd, 'cucumber.js');\n if (!existsSync(configPath)) {\n writeFileSync(configPath, CUCUMBER_CONFIG, 'utf-8');\n }\n\n mkdirSync(supportDir, { recursive: true });\n writeFileSync(supportPath, SUPPORT_FILE, 'utf-8');\n return 'created';\n}\n\nfunction setupFeaturesDir({ cwd }: Pick<Environment, 'cwd'>): boolean {\n if (existsSync(join(cwd, 'features'))) {\n try {\n const hasFeatureFiles = readdirSync(join(cwd, 'features')).some((f) => f.endsWith('.feature'));\n if (hasFeatureFiles) return false;\n } catch {\n return false;\n }\n }\n\n try {\n const hasFeatureAtRoot = readdirSync(cwd).some((f) => f.endsWith('.feature'));\n if (hasFeatureAtRoot) return false;\n } catch {\n return false;\n }\n\n mkdirSync(join(cwd, 'features'), { recursive: true });\n writeFileSync(join(cwd, 'features', 'example.feature'), EXAMPLE_FEATURE, 'utf-8');\n return true;\n}\n\nexport function setupCucumber(env: Pick<Environment, 'packageManager' | 'cwd'>): CucumberSetupResult {\n const bddInstalled = installBdd(env);\n const configResult = setupCucumberConfig(env);\n const featuresCreated = setupFeaturesDir(env);\n return { bddInstalled, configResult, featuresCreated };\n}\n","import { existsSync, mkdirSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { Environment } from '../detect.js';\n\nexport type GithubActionsResult = 'created' | 'skipped';\n\nfunction setupStepsFor({ packageManager, nodeVersion }: Pick<Environment, 'packageManager' | 'nodeVersion'>): string {\n if (packageManager === 'yarn') return `\\\n - name: Enable Corepack\n run: corepack enable\n - uses: actions/setup-node@v4\n with:\n node-version: ${nodeVersion}\n cache: yarn\n - name: Install dependencies\n run: yarn install --immutable`;\n if (packageManager === 'pnpm') return `\\\n - uses: pnpm/action-setup@v4\n - uses: actions/setup-node@v4\n with:\n node-version: ${nodeVersion}\n cache: pnpm\n - name: Install dependencies\n run: pnpm install --frozen-lockfile`;\n if (packageManager === 'bun') return `\\\n - uses: oven-sh/setup-bun@v2\n - name: Install dependencies\n run: bun install --frozen-lockfile`;\n return `\\\n - uses: actions/setup-node@v4\n with:\n node-version: ${nodeVersion}\n cache: npm\n - name: Install dependencies\n run: npm ci`;\n}\n\nfunction workflowYaml(env: Pick<Environment, 'packageManager' | 'nodeVersion'>): string {\n const setupSteps = setupStepsFor(env);\n\n return `name: Features\non:\n push:\n branches: [main]\n pull_request:\n branches: [main]\njobs:\n features:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v4\n${setupSteps}\n - name: Install Playwright browsers\n run: npx playwright install chromium --with-deps\n - name: Run features\n run: npx cucumber-js\n`;\n}\n\nexport function installGithubAction(env: Pick<Environment, 'packageManager' | 'nodeVersion' | 'cwd'>): GithubActionsResult {\n const workflowDir = join(env.cwd, '.github', 'workflows');\n const workflowPath = join(workflowDir, 'letsrunit.yml');\n\n if (existsSync(workflowPath)) return 'skipped';\n\n mkdirSync(workflowDir, { recursive: true });\n writeFileSync(workflowPath, workflowYaml(env), 'utf-8');\n return 'created';\n}\n","import { execSync } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { type Environment, execPm } from '../detect.js';\n\nexport function hasPlaywrightBrowsers({ cwd }: Pick<Environment, 'cwd'>): boolean {\n if (!existsSync(join(cwd, 'node_modules', 'playwright-core', 'package.json'))) {\n return false;\n }\n try {\n const execPath = execSync(\n `node -e \"console.log(require('playwright-core').chromium.executablePath())\"`,\n { cwd, stdio: 'pipe', encoding: 'utf-8' },\n ).trim();\n return existsSync(execPath);\n } catch {\n return false;\n }\n}\n\nexport function installPlaywrightBrowsers(env: Pick<Environment, 'packageManager' | 'cwd'>): void {\n execPm(env, {\n npm: 'exec playwright install chromium',\n yarn: 'exec playwright install chromium',\n pnpm: 'exec playwright install chromium',\n bun: 'x playwright install chromium',\n });\n}\n"],"mappings":"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","names":["existsSync","join","join","existsSync","existsSync","readFileSync","join","join","existsSync","readFileSync","existsSync","mkdirSync","writeFileSync","join","execSync","existsSync","join","existsSync","join","execSync","BDD_IMPORT"]}
|
package/package.json
CHANGED
|
@@ -1,11 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "letsrunit",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.3",
|
|
4
4
|
"description": "Onboarding tool for letsrunit — run npx letsrunit init to get started",
|
|
5
5
|
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/letsrunit-hq/letsrunit.git",
|
|
9
|
+
"directory": "packages/letsrunit"
|
|
10
|
+
},
|
|
11
|
+
"bugs": "https://github.com/letsrunit-hq/letsrunit/issues",
|
|
12
|
+
"homepage": "https://github.com/letsrunit-hq/letsrunit#readme",
|
|
6
13
|
"type": "module",
|
|
7
|
-
"bin":
|
|
8
|
-
|
|
14
|
+
"bin": {
|
|
15
|
+
"letsrunit": "./dist/bin.js"
|
|
16
|
+
},
|
|
17
|
+
"main": "./dist/index.js",
|
|
9
18
|
"publishConfig": {
|
|
10
19
|
"main": "./dist/index.js",
|
|
11
20
|
"bin": {
|
|
@@ -31,4 +40,4 @@
|
|
|
31
40
|
"@types/node": "^25.0.9",
|
|
32
41
|
"typescript": "^5.9.3"
|
|
33
42
|
}
|
|
34
|
-
}
|
|
43
|
+
}
|