import fs from 'node:fs/promises'; import os from 'node:os'; import assert from 'node:assert'; import path from 'node:path'; import { $ } from 'execa'; import { fileURLToPath } from 'node:url'; import { globby } from 'globby'; import { execa, type Options } from 'execa'; const DEBUG = process.env.DEBUG === 'true'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); // repo-root const blueprintPath = path.join(__dirname, '../..'); const defaultTryPath = path.join(blueprintPath, 'files/.try.mjs'); export const SUPPORTED_PACKAGE_MANAGERS = ['npm', 'pnpm'] as const; export async function getTryScenarios(filePath = defaultTryPath) { let scenarioInfo = await import(filePath); return scenarioInfo.default?.scenarios ?? []; } export async function applyTryScenario( tryName: string, options: { cwd: string; filePath?: string }, ) { let { filePath = defaultTryPath, cwd } = options; assert(cwd, 'applyTryScenario cwd is required'); let scenarios = await getTryScenarios(filePath); let validNames = scenarios.map((x: { name: string }) => x.name); assert( validNames.includes(tryName), `Invalid try scenario name: ${tryName}. Valid names: ${validNames.join(', ')}`, ); return await $({ cwd })`pnpm dlx @embroider/try apply ${tryName}`; } export async function createTmp() { let prefix = 'v2-addon-blueprint--'; let prefixPath = path.join(os.tmpdir(), prefix); let tmpDirPath = await fs.mkdtemp(prefixPath); return tmpDirPath; } /** * Returns a copy of the current process env with EMBER_, VITE_, and NODE_ * prefixed vars removed. * * Useful when spawning child processes with `extendEnv: false` so that * vitest's NODE_ENV=test (and similar) doesn't leak into the child and * interfere with the build-time / runtime macro mode detection. */ export function safeExecaEnv(): Record { let env = { ...process.env }; for (let key of Object.keys(env)) { if (key.startsWith('EMBER_') || key.startsWith('VITE_') || key.startsWith('NODE_')) { delete env[key]; } } return env; } /** * Abstraction for install, as the blueprint supports multiple package managers */ export async function install({ cwd, packageManager, skipPrepare, }: { cwd: string; packageManager: string; skipPrepare?: boolean; }) { if (packageManager === 'yarn') { await execa('yarn', ['install', '--non-interactive'], { cwd }); } else { let installOptions = []; if (packageManager === 'pnpm') { installOptions.push('--no-frozen-lockfile'); } await execa(packageManager, ['install', '--ignore-scripts', ...installOptions], { cwd }); } const pkg = await packageJsonAt(cwd); // in order to test prepare, we need to have ignore-scripts=false // which is a security risk so we'll manually invoke install + prepare if (pkg.scripts?.prepare && !skipPrepare) { await execa(packageManager, ['run', 'prepare'], { cwd }); } } /** * Abstraction for install, as the blueprint supports multiple package managers */ export async function runScript({ cwd, script, packageManager, }: { cwd: string; script: string; packageManager: string; }) { // all package managers allow a more verbose run