Skip to content

Commit

Permalink
fix(cli-create): fix scoped creation (#6239)
Browse files Browse the repository at this point in the history
* fix(cli-create): fix scoped creation

This one works as it should:
yarn create @scope/name => @scope/create-name

There were problems when name was omitted:

Before:
yarn create @scope => create-
yarn create @scope/ => @scope/create-

After:
yarn create @scope => @scope/create
yarn create @scope/ => @scope/create

Fixes #6233

* refactor(cli-create): make parsePackageName more robust and add tests

* refactor(cli-create): add missing typyings
  • Loading branch information
iamstarkov authored and arcanis committed Aug 13, 2018
1 parent 77b8444 commit acf82e2
Show file tree
Hide file tree
Showing 2 changed files with 135 additions and 4 deletions.
101 changes: 101 additions & 0 deletions __tests__/commands/create.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// @flow

import {parsePackageName, coerceCreatePackageName} from '../../src/cli/commands/create';

describe(`parsePackageName`, () => {
test('invalid', () => {
expect(() => {
parsePackageName('@/name');
}).toThrowError(`Scope should not be empty, got "@/name"`);
expect(() => {
parsePackageName('/name');
}).toThrowError(`Name should not start with "/", got "/name"`);
expect(() => {
parsePackageName('./name');
}).toThrowError(`Name should not start with ".", got "./name"`);
});

test('basic', () => {
expect(parsePackageName('name')).toEqual({
fullName: 'name',
name: 'name',
scope: '',
path: '',
full: 'name',
});
expect(parsePackageName('@scope/name')).toEqual({
fullName: '@scope/name',
name: 'name',
scope: '@scope',
path: '',
full: '@scope/name',
});
expect(parsePackageName('@scope/name/path/to/file.js')).toEqual({
fullName: '@scope/name',
name: 'name',
scope: '@scope',
path: 'path/to/file.js',
full: '@scope/name/path/to/file.js',
});
});

test('without name', () => {
expect(parsePackageName('@scope/')).toEqual({
fullName: '@scope',
name: '',
scope: '@scope',
path: '',
full: '@scope',
});
expect(parsePackageName('@scope')).toEqual({
fullName: '@scope',
name: '',
scope: '@scope',
path: '',
full: '@scope',
});
});
});

describe(`coerceCreatePackageName`, () => {
test('invalid', () => {
expect(() => {
coerceCreatePackageName('@/name');
}).toThrow();
expect(() => {
coerceCreatePackageName('/name');
}).toThrow();
expect(() => {
coerceCreatePackageName('./name');
}).toThrow();
});

test('basic', () => {
expect(coerceCreatePackageName('name')).toEqual({
fullName: 'create-name',
name: 'create-name',
scope: '',
path: '',
full: 'create-name',
});
expect(coerceCreatePackageName('@scope/name')).toEqual({
fullName: '@scope/create-name',
name: 'create-name',
scope: '@scope',
path: '',
full: '@scope/create-name',
});
expect(coerceCreatePackageName('@scope/name/path/to/file.js')).toEqual({
fullName: '@scope/create-name',
name: 'create-name',
scope: '@scope',
path: 'path/to/file.js',
full: '@scope/create-name/path/to/file.js',
});
});

test('not postfixing with "-" if name is emptu', () => {
expect(coerceCreatePackageName('@scope/').fullName).toEqual('@scope/create');
expect(coerceCreatePackageName('@scope').fullName).toEqual('@scope/create');
});
});
38 changes: 34 additions & 4 deletions src/cli/commands/create.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,47 @@ export function hasWrapper(commander: Object, args: Array<string>): boolean {
return true;
}

export function parsePackageName(str: string): Object {
if (str.charAt(0) === '/') {
throw new Error(`Name should not start with "/", got "${str}"`);
}
if (str.charAt(0) === '.') {
throw new Error(`Name should not start with ".", got "${str}"`);
}
const parts = str.split('/');
const isScoped = str.charAt(0) === '@';
if (isScoped && parts[0] === '@') {
throw new Error(`Scope should not be empty, got "${str}"`);
}
const scope = isScoped ? parts[0] : '';
const name = parts[isScoped ? 1 : 0] || '';
const path = parts.slice(isScoped ? 2 : 1).join('/');
const fullName = [scope, name].filter(Boolean).join('/');
const full = [scope, name, path].filter(Boolean).join('/');

return {fullName, name, scope, path, full};
}

export function coerceCreatePackageName(str: string): Object {
const pkgNameObj = parsePackageName(str);
const coercedName = pkgNameObj.name !== '' ? `create-${pkgNameObj.name}` : `create`;
const coercedPkgNameObj = {
...pkgNameObj,
name: coercedName,
fullName: [pkgNameObj.scope, coercedName].filter(Boolean).join('/'),
full: [pkgNameObj.scope, coercedName, pkgNameObj.path].filter(Boolean).join('/'),
};
return coercedPkgNameObj;
}

export async function run(config: Config, reporter: Reporter, flags: Object, args: Array<string>): Promise<void> {
const [builderName, ...rest] = args;

if (!builderName) {
throw new MessageError(reporter.lang('invalidPackageName'));
}

const packageName = builderName.replace(/^(@[^\/]+\/)?/, '$1create-');
const packageDir = packageName.replace(/^(?:(@[^\/]+)\/)?.*/, '$1');
const commandName = packageName.replace(/^@[^\/]+\//, '');

const {fullName: packageName, scope: packageDir, name: commandName} = coerceCreatePackageName(builderName);
await runGlobal(config, reporter, {}, ['add', packageName]);

const binFolder = await getBinFolder(config, {});
Expand Down

0 comments on commit acf82e2

Please sign in to comment.