UNPKG

derw

Version:

An Elm-inspired language that transpiles to TypeScript

274 lines (234 loc) 7.82 kB
import { allErrors, bothFlag, empty, help, longFlag, parse, parser, string, } from "@eeue56/baner"; import { Err, Ok, Result } from "@eeue56/ts-core/build/main/lib/result"; import { spawnSync } from "child_process"; import { writeFile } from "fs/promises"; import fetch from "node-fetch"; import { addDependency, decodePackage, Dependency, exportPackage, loadPackageFile, Package, } from "../package"; import { ensureDirectoryExists, fileExists } from "./utils"; const installParser = parser([ longFlag("name", "name of the package e.g derw-lang/stdlib", string()), longFlag("version", "name of the package e.g main or master", string()), longFlag("quiet", "Keep it short and sweet", empty()), bothFlag("h", "help", "This help text", empty()), ]); function showInstallHelp(): void { console.log( "To install a new package run `derw install --name {package name} --version {version}`" ); console.log( "Or run me without args inside a package directory to install all packages in derw-package.json" ); console.log(help(installParser)); } export async function install( isInPackageDirectory: boolean, argv: string[] ): Promise<void> { if (!isInPackageDirectory) { console.log( "No derw-package.json found. Maybe you need to run `derw init` first?" ); process.exit(1); } const program = parse(installParser, argv); if (program.flags["h/help"].isPresent) { showInstallHelp(); return; } const isQuiet = program.flags.quiet.isPresent; const isInstallNewPackage = program.flags.name.isPresent; if (argv.length > 3 && !(isInstallNewPackage || isQuiet)) { showInstallHelp(); return; } const packageFile = await loadPackageFile("derw-package.json"); if (packageFile.kind === "Err") { console.log("Failed to parse package file due to:"); console.log(packageFile.error); process.exit(1); } const errors = allErrors(program); if (errors.length > 0) { console.log("Errors:"); console.log(errors.join("\n")); process.exit(1); } let validPackage = packageFile.value; if (isInstallNewPackage) { const name = (program.flags.name.arguments as Ok<string>).value; let version = "main"; if (program.flags.version.isPresent) { version = (program.flags.version.arguments as Ok<string>).value; } if (!isQuiet) console.log("Reading derw-package.json..."); validPackage = addDependency(Dependency(name, version), validPackage); if (!isQuiet) { console.log( `Writing new package ${name}@${version} to derw-package.json...` ); } await writeFile("derw-package.json", exportPackage(validPackage)); if (!isQuiet) console.log("Done!"); } if (validPackage.dependencies.length === 0) { console.log("No dependencies to install!"); return; } else { if (!isQuiet) console.log("Installing packages..."); await installPackages(validPackage, isQuiet, [ ]); if (!isQuiet) console.log( `Installed ${validPackage.dependencies.length} packages` ); } } function isPackageAlreadyThere( package_: Package, packages: Package[] ): boolean { for (const p of packages) { if (package_.name === p.name) return true; } return false; } function isAlreadyInstalled( dependency: Dependency, alreadyInstalledDependency: Dependency[] ): boolean { return ( alreadyInstalledDependency.filter( (d) => d.name === dependency.name && d.version === dependency.version ).length > 0 ); } async function installPackages( validPackage: Package, isQuiet: boolean, alreadyInstalledDependencies: Dependency[] ): Promise<Package[]> { await ensureDirectoryExists("derw-packages"); const installedPackages: Package[] = [ ]; for (const dependency of validPackage.dependencies) { const alreadyInstalled = isAlreadyInstalled( dependency, alreadyInstalledDependencies ); if (alreadyInstalled) continue; if (!isQuiet) console.log(`Fetching ${dependency.name}@${dependency.version}...`); const depPackage = await fetchDependencyPackage(dependency); if (depPackage.kind === "Ok") { if (!isPackageAlreadyThere(depPackage.value, installedPackages)) { const subpackages = await installPackages( depPackage.value, isQuiet, [ ...alreadyInstalledDependencies, ...validPackage.dependencies, ] ); for (const subpackage of subpackages) { installedPackages.push(subpackage); } } } await cloneRepo(dependency); await checkoutRef(dependency); if (await fileExists(`derw-packages/${dependency.name}/package.json`)) { if (!isQuiet) console.log("Installing npm packages..."); await npmInstall(dependency); } if (depPackage.kind === "Ok") { installedPackages.push(depPackage.value); } } return installedPackages; } async function cloneRepo(dependency: Dependency): Promise<void> { await ensureDirectoryExists(`derw-packages/${dependency.name}`); const res = spawnSync( "git", [ "clone", `https://github.com/${dependency.name}.git`, dependency.name, ], { cwd: "derw-packages", encoding: "utf-8" } ); if (res.error) { console.log(`Encountered error cloning ${dependency.name}`); console.log(res.error); } } async function checkoutRef(dependency: Dependency) { let res = spawnSync("git", [ "fetch", `origin`, `${dependency.version}` ], { cwd: `derw-packages/${dependency.name}`, encoding: "utf-8", }); if (res.error) { console.log(`Encountered error fetching ${dependency.name}`); console.log(res.error); } res = spawnSync( "git", [ "reset", "--hard", `origin/${dependency.version}` ], { cwd: `derw-packages/${dependency.name}`, encoding: "utf-8", } ); if (res.error) { console.log(`Encountered error checkout ${dependency.name}`); console.log(res.error); } res = spawnSync("git", [ "reset", "--hard", `${dependency.version}` ], { cwd: `derw-packages/${dependency.name}`, encoding: "utf-8", }); if (res.error) { console.log(`Encountered error checkout ${dependency.name}`); console.log(res.error); } } async function npmInstall(dependency: Dependency): Promise<void> { const res = spawnSync("npm", [ "install" ], { cwd: `derw-packages/${dependency.name}`, encoding: "utf-8", }); if (res.error) { console.log( `Encountered error installing npm packages from ${dependency.name}` ); console.log(res.error); } } async function fetchDependencyPackage( dependency: Dependency ): Promise<Result<string, Package>> { try { const response = await fetch( `https://raw.githubusercontent.com/${dependency.name}/${dependency.version}/derw-package.json` ); return decodePackage(await response.json()); } catch (error) { console.log(error); return Err(`Failed to fetch ${dependency.name}@${dependency.version}`); } }