Skip to content

Commit acf82e2

Browse files
iamstarkovarcanis
authored andcommitted
fix(cli-create): fix scoped creation (#6239)
* 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
1 parent 77b8444 commit acf82e2

File tree

2 files changed

+135
-4
lines changed

2 files changed

+135
-4
lines changed

__tests__/commands/create.js

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
// @flow
2+
3+
import {parsePackageName, coerceCreatePackageName} from '../../src/cli/commands/create';
4+
5+
describe(`parsePackageName`, () => {
6+
test('invalid', () => {
7+
expect(() => {
8+
parsePackageName('@/name');
9+
}).toThrowError(`Scope should not be empty, got "@/name"`);
10+
expect(() => {
11+
parsePackageName('/name');
12+
}).toThrowError(`Name should not start with "/", got "/name"`);
13+
expect(() => {
14+
parsePackageName('./name');
15+
}).toThrowError(`Name should not start with ".", got "./name"`);
16+
});
17+
18+
test('basic', () => {
19+
expect(parsePackageName('name')).toEqual({
20+
fullName: 'name',
21+
name: 'name',
22+
scope: '',
23+
path: '',
24+
full: 'name',
25+
});
26+
expect(parsePackageName('@scope/name')).toEqual({
27+
fullName: '@scope/name',
28+
name: 'name',
29+
scope: '@scope',
30+
path: '',
31+
full: '@scope/name',
32+
});
33+
expect(parsePackageName('@scope/name/path/to/file.js')).toEqual({
34+
fullName: '@scope/name',
35+
name: 'name',
36+
scope: '@scope',
37+
path: 'path/to/file.js',
38+
full: '@scope/name/path/to/file.js',
39+
});
40+
});
41+
42+
test('without name', () => {
43+
expect(parsePackageName('@scope/')).toEqual({
44+
fullName: '@scope',
45+
name: '',
46+
scope: '@scope',
47+
path: '',
48+
full: '@scope',
49+
});
50+
expect(parsePackageName('@scope')).toEqual({
51+
fullName: '@scope',
52+
name: '',
53+
scope: '@scope',
54+
path: '',
55+
full: '@scope',
56+
});
57+
});
58+
});
59+
60+
describe(`coerceCreatePackageName`, () => {
61+
test('invalid', () => {
62+
expect(() => {
63+
coerceCreatePackageName('@/name');
64+
}).toThrow();
65+
expect(() => {
66+
coerceCreatePackageName('/name');
67+
}).toThrow();
68+
expect(() => {
69+
coerceCreatePackageName('./name');
70+
}).toThrow();
71+
});
72+
73+
test('basic', () => {
74+
expect(coerceCreatePackageName('name')).toEqual({
75+
fullName: 'create-name',
76+
name: 'create-name',
77+
scope: '',
78+
path: '',
79+
full: 'create-name',
80+
});
81+
expect(coerceCreatePackageName('@scope/name')).toEqual({
82+
fullName: '@scope/create-name',
83+
name: 'create-name',
84+
scope: '@scope',
85+
path: '',
86+
full: '@scope/create-name',
87+
});
88+
expect(coerceCreatePackageName('@scope/name/path/to/file.js')).toEqual({
89+
fullName: '@scope/create-name',
90+
name: 'create-name',
91+
scope: '@scope',
92+
path: 'path/to/file.js',
93+
full: '@scope/create-name/path/to/file.js',
94+
});
95+
});
96+
97+
test('not postfixing with "-" if name is emptu', () => {
98+
expect(coerceCreatePackageName('@scope/').fullName).toEqual('@scope/create');
99+
expect(coerceCreatePackageName('@scope').fullName).toEqual('@scope/create');
100+
});
101+
});

src/cli/commands/create.js

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,47 @@ export function hasWrapper(commander: Object, args: Array<string>): boolean {
1616
return true;
1717
}
1818

19+
export function parsePackageName(str: string): Object {
20+
if (str.charAt(0) === '/') {
21+
throw new Error(`Name should not start with "/", got "${str}"`);
22+
}
23+
if (str.charAt(0) === '.') {
24+
throw new Error(`Name should not start with ".", got "${str}"`);
25+
}
26+
const parts = str.split('/');
27+
const isScoped = str.charAt(0) === '@';
28+
if (isScoped && parts[0] === '@') {
29+
throw new Error(`Scope should not be empty, got "${str}"`);
30+
}
31+
const scope = isScoped ? parts[0] : '';
32+
const name = parts[isScoped ? 1 : 0] || '';
33+
const path = parts.slice(isScoped ? 2 : 1).join('/');
34+
const fullName = [scope, name].filter(Boolean).join('/');
35+
const full = [scope, name, path].filter(Boolean).join('/');
36+
37+
return {fullName, name, scope, path, full};
38+
}
39+
40+
export function coerceCreatePackageName(str: string): Object {
41+
const pkgNameObj = parsePackageName(str);
42+
const coercedName = pkgNameObj.name !== '' ? `create-${pkgNameObj.name}` : `create`;
43+
const coercedPkgNameObj = {
44+
...pkgNameObj,
45+
name: coercedName,
46+
fullName: [pkgNameObj.scope, coercedName].filter(Boolean).join('/'),
47+
full: [pkgNameObj.scope, coercedName, pkgNameObj.path].filter(Boolean).join('/'),
48+
};
49+
return coercedPkgNameObj;
50+
}
51+
1952
export async function run(config: Config, reporter: Reporter, flags: Object, args: Array<string>): Promise<void> {
2053
const [builderName, ...rest] = args;
2154

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

26-
const packageName = builderName.replace(/^(@[^\/]+\/)?/, '$1create-');
27-
const packageDir = packageName.replace(/^(?:(@[^\/]+)\/)?.*/, '$1');
28-
const commandName = packageName.replace(/^@[^\/]+\//, '');
29-
59+
const {fullName: packageName, scope: packageDir, name: commandName} = coerceCreatePackageName(builderName);
3060
await runGlobal(config, reporter, {}, ['add', packageName]);
3161

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

0 commit comments

Comments
 (0)