1
0
mirror of synced 2025-11-08 12:57:47 +00:00
Files
typescript-starter/src/cli/tasks.ts
2018-03-15 22:55:40 -04:00

211 lines
5.7 KiB
TypeScript

// tslint:disable:no-console no-if-statement no-expression-statement
import execa, { ExecaStatic, Options, StdIOOption } from 'execa';
import githubUsername from 'github-username';
import { join } from 'path';
import {
Runner,
TypescriptStarterInferredOptions,
TypescriptStarterOptions,
TypescriptStarterUserOptions
} from './utils';
// TODO: await https://github.com/DefinitelyTyped/DefinitelyTyped/pull/24209
const inherit = 'inherit' as StdIOOption;
export enum Placeholders {
email = 'YOUR_EMAIL',
name = 'YOUR_NAME',
username = 'YOUR_GITHUB_USER_NAME'
}
// We implement these as function factories to make unit testing easier.
export const cloneRepo = (
spawner: ExecaStatic,
suppressOutput = false
) => async (
repoInfo: {
readonly branch: string;
readonly repo: string;
},
workingDirectory: string,
dir: string
) => {
const projectDir = join(workingDirectory, dir);
const gitHistoryDir = join(projectDir, '.git');
try {
await spawner(
'git',
['clone', '--depth=1', `--branch=${repoInfo.branch}`, repoInfo.repo, dir],
{
cwd: workingDirectory,
stdio: suppressOutput ? 'pipe' : 'inherit'
}
);
} catch (err) {
if (err.code === 'ENOENT') {
throw new Error(`
Git is not installed on your PATH. Please install Git and try again.
For more information, visit: https://git-scm.com/book/en/v2/Getting-Started-Installing-Git
`);
} else {
throw new Error(`Git clone failed.`);
}
}
try {
const revParseResult = await spawner('git', ['rev-parse', 'HEAD'], {
cwd: projectDir,
encoding: 'utf8',
stdio: ['pipe', 'pipe', inherit]
});
const commitHash = revParseResult.stdout;
return { commitHash, gitHistoryDir };
} catch (err) {
throw new Error(`Git rev-parse failed.`);
}
};
export const getGithubUsername = (fetcher: any) => async (
email: string | undefined
): Promise<string> => {
if (email === Placeholders.email) {
return Placeholders.username;
}
return fetcher(email).catch(() => {
return Placeholders.username;
});
};
export const getUserInfo = (spawner: ExecaStatic) => async () => {
const opts: Options = {
encoding: 'utf8',
stdio: ['pipe', 'pipe', inherit]
};
try {
const nameResult = await spawner('git', ['config', 'user.name'], opts);
const emailResult = await spawner('git', ['config', 'user.email'], opts);
return {
gitEmail: emailResult.stdout,
gitName: nameResult.stdout
};
} catch (err) {
return {
gitEmail: Placeholders.email,
gitName: Placeholders.name
};
}
};
export const initialCommit = (spawner: ExecaStatic) => async (
hash: string,
projectDir: string
): Promise<void> => {
const opts: Options = {
cwd: projectDir,
encoding: 'utf8',
stdio: 'pipe'
};
await spawner('git', ['init'], opts);
await spawner('git', ['add', '-A'], opts);
await spawner(
'git',
[
'commit',
'-m',
`Initial commit\n\nCreated with bitjson/typescript-starter@${hash}`
],
opts
);
};
export const install = (spawner: ExecaStatic) => async (
runner: Runner,
projectDir: string
) => {
const opts: Options = {
cwd: projectDir,
encoding: 'utf8',
stdio: 'inherit'
};
try {
runner === Runner.Npm
? spawner('npm', ['install'], opts)
: spawner('yarn', opts);
} catch (err) {
throw new Error(`Installation failed. You'll need to install manually.`);
}
};
/**
* Returns the URL and branch to clone. We clone the branch (tag) at the current
* release rather than `master`. This ensures we get the exact files expected by
* this version of the CLI. (If we cloned master, changes merged to master, but
* not yet released, may cause unexpected results.)
* @param starterVersion the current version of this CLI
*/
export const getRepoInfo = (starterVersion: string) => {
return process.env.TYPESCRIPT_STARTER_REPO_URL
? {
branch: process.env.TYPESCRIPT_STARTER_REPO_BRANCH
? process.env.TYPESCRIPT_STARTER_REPO_BRANCH
: 'master',
repo: process.env.TYPESCRIPT_STARTER_REPO_URL
}
: {
branch: `v${starterVersion}`,
repo: 'https://github.com/bitjson/typescript-starter.git'
};
};
export interface Tasks {
readonly cloneRepo: (
repoInfo: {
readonly branch: string;
readonly repo: string;
},
workingDirectory: string,
dir: string
) => Promise<{ readonly commitHash: string; readonly gitHistoryDir: string }>;
readonly initialCommit: (
hash: string,
projectDir: string,
name: string
) => Promise<void>;
readonly install: (runner: Runner, projectDir: string) => Promise<void>;
}
export const LiveTasks: Tasks = {
cloneRepo: cloneRepo(execa),
initialCommit: initialCommit(execa),
install: install(execa)
};
export const addInferredOptions = async (
userOptions: TypescriptStarterUserOptions
): Promise<TypescriptStarterOptions> => {
const { gitName, gitEmail } = await getUserInfo(execa)();
const username = await getGithubUsername(githubUsername)(gitEmail);
const inferredOptions: TypescriptStarterInferredOptions = {
email: gitEmail,
fullName: gitName,
githubUsername: username,
repoInfo: getRepoInfo(userOptions.starterVersion),
workingDirectory: process.cwd()
};
return {
...inferredOptions,
appveyor: userOptions.appveyor,
circleci: userOptions.circleci,
description: userOptions.description,
domDefinitions: userOptions.domDefinitions,
immutable: userOptions.immutable,
install: userOptions.install,
nodeDefinitions: userOptions.nodeDefinitions,
projectName: userOptions.projectName,
runner: userOptions.runner,
strict: userOptions.strict,
travis: userOptions.travis,
vscode: userOptions.vscode
};
};