Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Yarn #285

Merged
merged 3 commits into from
Mar 1, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import * as meow from 'meow';
import * as updateNotifier from 'update-notifier';
import { init } from './init';
import { clean } from './clean';
import { isYarnUsed } from './util';

const packageJson = require('../../package.json');

Expand All @@ -35,6 +36,7 @@ export interface Options {
yes: boolean;
no: boolean;
logger: Logger;
yarn?: boolean;
}

export type VerbFilesFunction = (
Expand All @@ -60,7 +62,8 @@ const cli = meow({
--help Prints this help message.
-y, --yes Assume a yes answer for every prompt.
-n, --no Assume a no answer for every prompt.
--dry-run Don't make any acutal changes.
--dry-run Don't make any actual changes.
--yarn Use yarn instead of npm.

Examples
$ gts init -y
Expand All @@ -73,6 +76,7 @@ const cli = meow({
yes: { type: 'boolean', alias: 'y' },
no: { type: 'boolean', alias: 'n' },
'dry-run': { type: 'boolean' },
yarn: { type: 'boolean' },
},
});

Expand All @@ -92,6 +96,7 @@ async function run(verb: string, files: string[]): Promise<boolean> {
yes: cli.flags.yes || cli.flags.y || false,
no: cli.flags.no || cli.flags.n || false,
logger,
yarn: cli.flags.yarn || isYarnUsed(),
};
// Linting/formatting depend on typescript. We don't want to load the
// typescript module during init, since it might not exist.
Expand Down
14 changes: 10 additions & 4 deletions src/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
readFilep as read,
readJsonp as readJson,
writeFileAtomicp as write,
getPkgManagerName,
} from './util';

const pkg = require('../../package.json');
Expand Down Expand Up @@ -88,14 +89,15 @@ export async function addScripts(
options: Options
): Promise<boolean> {
let edits = false;
const pkgManager = getPkgManagerName(options.yarn);
const scripts: Bag<string> = {
check: `gts check`,
clean: 'gts clean',
compile: `tsc -p .`,
fix: `gts fix`,
prepare: `npm run compile`,
pretest: `npm run compile`,
posttest: `npm run check`,
prepare: `${pkgManager} run compile`,
pretest: `${pkgManager} run compile`,
posttest: `${pkgManager} run check`,
};

if (!packageJson.scripts) {
Expand Down Expand Up @@ -292,7 +294,11 @@ export async function init(options: Options): Promise<boolean> {
if (!options.dryRun) {
// --ignore-scripts so that compilation doesn't happen because there's no
// source files yet.
cp.spawnSync('npm', ['install', '--ignore-scripts'], { stdio: 'inherit' });
cp.spawnSync(
getPkgManagerName(options.yarn),
['install', '--ignore-scripts'],
{ stdio: 'inherit' }
);
}

return true;
Expand Down
16 changes: 16 additions & 0 deletions src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,3 +124,19 @@ export interface ConfigFile {
exclude?: string[];
extends?: string[];
}

/**
* Automatically defines npm or yarn is going to be used:
* - If only yarn.lock exists, use yarn
* - If only package-lock.json or both exist, use npm
*/
export function isYarnUsed(existsSync = fs.existsSync): boolean {
if (existsSync('package-lock.json')) {
return false;
}
return existsSync('yarn.lock');
}

export function getPkgManagerName(isYarnUsed?: boolean): 'yarn' | 'npm' {
return isYarnUsed ? 'yarn' : 'npm';
}
18 changes: 17 additions & 1 deletion test/test-init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const OPTIONS: Options = {
};
const OPTIONS_YES = Object.assign({}, OPTIONS, { yes: true });
const OPTIONS_NO = Object.assign({}, OPTIONS, { no: true });
const OPTIONS_DRY_RUN = Object.assign({}, OPTIONS, { dryRun: true });
const OPTIONS_YARN = Object.assign({}, OPTIONS_YES, { yarn: true });

function hasExpectedScripts(packageJson: init.PackageJson): boolean {
return (
Expand Down Expand Up @@ -161,4 +161,20 @@ test.serial('init should handle missing package.json', t => {
});
});

test.serial('init should support yarn', t => {
return withFixtures(
{
'package.json': JSON.stringify({ name: 'test' }),
'yarn.lock': '',
},
async () => {
const result = await init.init(OPTIONS_YARN);
t.truthy(result);

const contents = await readJson('./package.json');
t.truthy(contents.scripts.prepare === 'yarn run compile');
}
);
});

// TODO: need more tests.
41 changes: 40 additions & 1 deletion test/test-util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,14 @@
*/
import test from 'ava';
import * as path from 'path';
import { PathLike } from 'fs';

import { ConfigFile, getTSConfig } from '../src/util';
import {
ConfigFile,
getTSConfig,
isYarnUsed,
getPkgManagerName,
} from '../src/util';

/**
* Creates a fake promisified readFile function from a map
Expand All @@ -35,6 +41,12 @@ function createFakeReadFilep(myMap: Map<string, ConfigFile>) {
};
}

function makeFakeFsExistsSync(
expected: PathLike[]
): (path: PathLike) => boolean {
return (path: PathLike) => expected.some(item => item === path);
}

test('get should parse the correct tsconfig file', async t => {
const FAKE_DIRECTORY = '/some/fake/directory';
const FAKE_CONFIG1 = { files: ['b'] };
Expand Down Expand Up @@ -148,4 +160,31 @@ test('function throws an error when reading a file that does not exist', async t
);
});

test("isYarnUsed returns true if there's yarn.lock file only", async t => {
const existsSync = makeFakeFsExistsSync(['yarn.lock']);

t.is(isYarnUsed(existsSync), true);
});

test("isYarnUsed returns false if there's package-lock.json file only", async t => {
const existsSync = makeFakeFsExistsSync(['package-lock.json']);

t.is(isYarnUsed(existsSync), false);
});

test("isYarnUsed returns false if there're yarn.lock and package-lock.json files", async t => {
const existsSync = makeFakeFsExistsSync(['package-lock.json', 'yarn.lock']);

t.is(isYarnUsed(existsSync), false);
});

test('getPkgManagerName returns npm by default', async t => {
t.is(getPkgManagerName(), 'npm');
t.is(getPkgManagerName(), getPkgManagerName(false));
});

test('getPkgManagerName returns yarn', async t => {
t.is(getPkgManagerName(true), 'yarn');
});

// TODO: test errors in readFile, JSON.parse.