-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* drop `yarn` support as it doesn't support peer dependency installation * drop `ora` and add proper messaging * change git branch to `main` * add initial git commit * merge `ProjectPackage` back into `sync`
- Loading branch information
Paul
committed
Nov 11, 2021
1 parent
d8383ed
commit 122bdcf
Showing
8 changed files
with
231 additions
and
404 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,157 +1,83 @@ | ||
import { exec, SpawnOptions } from 'child_process'; | ||
import { rm, mkdir } from 'fs/promises'; | ||
|
||
import npa from 'npm-package-arg'; | ||
import readline from 'readline'; | ||
import type { Result as NpaResult } from 'npm-package-arg'; | ||
import crossSpawn from 'cross-spawn'; | ||
import { readPackageJson } from '@pota/shared/fs'; | ||
import kleur from 'kleur'; | ||
|
||
import { SPINNER } from './spinner.js'; | ||
import { readPackageJson, isDirectoryAvailable } from '@pota/shared/fs'; | ||
|
||
const { green, cyan } = kleur; | ||
|
||
export const newline = () => console.log(); | ||
|
||
export const log = (text: string) => console.log(text); | ||
|
||
export function clear() { | ||
if (process.stdout.isTTY) { | ||
console.log('\n'.repeat(process.stdout.rows)); | ||
readline.cursorTo(process.stdout, 0, 0); | ||
readline.clearScreenDown(process.stdout); | ||
} | ||
} | ||
export const log = (text: string = '') => console.log(text); | ||
|
||
const createSpawn = (options: SpawnOptions) => | ||
async function spawn(command: string, ...args: string[]): Promise<void> { | ||
await new Promise<void>((resolve, reject) => | ||
function createSpawn(options: SpawnOptions) { | ||
return (command: string, ...args: string[]): Promise<void> => | ||
new Promise<void>((resolve, reject) => | ||
crossSpawn(command, args, options) | ||
.on('close', (code) => | ||
code !== 0 ? reject({ command: `${command} ${args.join(' ')}` }) : resolve(), | ||
) | ||
.on('error', reject), | ||
); | ||
}; | ||
} | ||
|
||
export const spawn = createSpawn({ stdio: 'inherit' }); | ||
export const spawnSilent = createSpawn({ stdio: 'ignore' }); | ||
|
||
export const command = (command: string, quiet: boolean = true) => | ||
new Promise<string | undefined>((resolve, reject) => | ||
export function command(command: string, quiet: boolean = true) { | ||
return new Promise<string | undefined>((resolve, reject) => | ||
exec(command, (error, stdout, stderr) => { | ||
if (error) { | ||
return reject(error); | ||
} | ||
if (error) return reject(error); | ||
resolve(quiet ? undefined : stdout || stderr); | ||
}), | ||
); | ||
|
||
export function isValidSkeleton(skeleton: string) { | ||
try { | ||
return Boolean(npa(skeleton)); | ||
} catch (error) { | ||
return false; | ||
} | ||
} | ||
|
||
export function isFileSkeleton(skeleton: string) { | ||
return npa(skeleton).type === 'file'; | ||
} | ||
|
||
type PackageManager = 'yarn' | 'npm'; | ||
|
||
function getPackageManager(): PackageManager { | ||
const userAgent = process.env.npm_config_user_agent; | ||
|
||
if (userAgent?.startsWith('npm')) { | ||
return 'npm'; | ||
} | ||
|
||
if (userAgent?.startsWith('yarn')) { | ||
return 'yarn'; | ||
} | ||
|
||
return 'npm'; | ||
} | ||
|
||
interface InstallOptions { | ||
cwd?: string; | ||
dev?: boolean; | ||
npm?: boolean; | ||
yarn?: boolean; | ||
} | ||
|
||
export function createInstaller(options: InstallOptions = {}) { | ||
const { cwd, dev, yarn, npm } = options; | ||
let pre: Array<string> = []; | ||
let post: Array<string> = []; | ||
|
||
const pm = !yarn && !npm ? getPackageManager() : yarn ? 'yarn' : 'npm'; | ||
|
||
switch (pm) { | ||
case 'npm': { | ||
pre = ['install']; | ||
if (cwd) post.push('--prefix', cwd); | ||
if (dev) post.push('--save-dev'); | ||
// reduce terminal noise | ||
post.push('--no-audit', '--no-fund'); | ||
break; | ||
} | ||
case 'yarn': { | ||
pre = ['add']; | ||
if (cwd) post.push('--cwd', cwd); | ||
if (dev) post.push('--dev'); | ||
break; | ||
} | ||
} | ||
|
||
return async (...packages: ReadonlyArray<string>) => { | ||
// we must use `install` instead of `add` with `yarn` | ||
// when installing `package.json` deps | ||
switch (pm) { | ||
case 'yarn': | ||
if (packages.length === 0) pre[0] = 'install'; | ||
break; | ||
export function createBailer(dir?: string) { | ||
return async () => { | ||
if (dir) { | ||
try { | ||
log(); | ||
console.error('Deleting created directory.'); | ||
await rm(dir, { recursive: true }); | ||
} catch {} | ||
} | ||
|
||
SPINNER.stopAndPersist(); | ||
|
||
try { | ||
await spawn(pm, ...pre, ...packages, ...post); | ||
} finally { | ||
SPINNER.start(); | ||
} | ||
process.exit(1); | ||
}; | ||
} | ||
|
||
export async function getSkeletonName(rawSkeletonName: string, packageJsonPath: string) { | ||
const parsedName = npa(rawSkeletonName); | ||
export async function createDir(path: string) { | ||
if (await isDirectoryAvailable(path)) await mkdir(path, { recursive: true }); | ||
else throw new Error(`${green(path)} already exists, please specify a different directory`); | ||
} | ||
|
||
export async function getSkeletonName(skeletonPkgDetails: NpaResult, packageJsonPath: string) { | ||
const { dependencies = {}, devDependencies = {} } = await readPackageJson(packageJsonPath); | ||
|
||
const dependency = [dependencies, devDependencies] | ||
.flatMap((d) => Object.entries(d)) | ||
.find(([name, version]) => { | ||
switch (parsedName.type) { | ||
switch (skeletonPkgDetails.type) { | ||
case 'git': { | ||
const { gitCommittish, hosted } = parsedName; | ||
const { gitCommittish, hosted } = skeletonPkgDetails; | ||
const { user, type, project } = hosted!; | ||
|
||
// github:mediamonks/pota#feature | ||
return ( | ||
version === `${type}:${user}/${project}#${gitCommittish}` || | ||
version === parsedName.rawSpec | ||
version === skeletonPkgDetails.rawSpec | ||
); | ||
} | ||
case 'file': | ||
return version === parsedName.saveSpec; | ||
return version === skeletonPkgDetails.saveSpec; | ||
default: | ||
return name === rawSkeletonName; | ||
return name === skeletonPkgDetails.raw; | ||
} | ||
}); | ||
|
||
if (!dependency) | ||
throw new Error(`Could not find ${cyan(rawSkeletonName)} in ${green(packageJsonPath)}`); | ||
if (!dependency) { | ||
throw new Error(`Could not find ${cyan(skeletonPkgDetails.raw)} in ${green(packageJsonPath)}`); | ||
} | ||
|
||
return dependency[0]; // the name of the dependency | ||
} |
Oops, something went wrong.