Skip to content

Commit d565215

Browse files
SevenOutmanjimthedev
authored andcommitted
feat: support initialization with yarn. fixes #527 (#549)
Support `commitizen init` with `yarn` by adding `--yarn` flag. See discussion here #527. fixes #527
1 parent a70c234 commit d565215

File tree

6 files changed

+220
-34
lines changed

6 files changed

+220
-34
lines changed

README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,15 @@ Next, initialize your project to use the cz-conventional-changelog adapter by ty
7979
commitizen init cz-conventional-changelog --save-dev --save-exact
8080
```
8181

82+
Or if you are using Yarn:
83+
84+
```
85+
commitizen init cz-conventional-changelog --yarn --dev --exact
86+
```
87+
8288
Note that if you want to force install over the top of an old adapter, you can apply the `--force` argument. For more information on this, just run `commitizen help`.
8389

84-
The above command does three things for you.
90+
The above command does three things for you.
8591
1. Installs the cz-conventional-changelog adapter npm module
8692
2. Saves it to package.json's dependencies or devDependencies
8793
3. Adds the `config.commitizen` key to the root of your **package.json** as shown here:

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
"semantic-release": "semantic-release pre && npm publish && semantic-release post",
1515
"start": "npm run test:watch",
1616
"lint": "eslint --ignore-path .gitignore .",
17-
"test": "nyc --require babel-core/register _mocha -- test/tests/index.js",
17+
"test": "nyc --require ./test/helpers/register _mocha -- test/tests/index.js",
1818
"test:watch": "nodemon -q --ignore test/.tmp/ --ignore test/artifacts/ --ignore coverage/ --exec \"npm run test\" --",
1919
"test:windows": "node ./test/tools/trigger-appveyor-tests.js"
2020
},

src/commitizen/adapter.js

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ export {
1414
getNpmInstallStringMappings,
1515
getPrompter,
1616
generateNpmInstallAdapterCommand,
17-
resolveAdapterPath
17+
resolveAdapterPath,
18+
getYarnAddStringMappings,
19+
generateYarnAddAdapterCommand,
1820
};
1921

2022
/**
@@ -70,6 +72,24 @@ function generateNpmInstallAdapterCommand (stringMappings, adapterNpmName) {
7072
return installAdapterCommand;
7173
}
7274

75+
/**
76+
* Generates an yarn add command given a map of strings and a package name
77+
*/
78+
function generateYarnAddAdapterCommand (stringMappings, adapterNpmName) {
79+
80+
// Start with an initial yarn add command
81+
let installAdapterCommand = `yarn add ${adapterNpmName}`;
82+
83+
// Append the necessary arguments to it based on user preferences
84+
for (let [key, value] of stringMappings.entries()) {
85+
if (value) {
86+
installAdapterCommand = installAdapterCommand + ' ' + value;
87+
}
88+
}
89+
90+
return installAdapterCommand;
91+
}
92+
7393
/**
7494
* Gets the nearest npm_modules directory
7595
*/
@@ -106,6 +126,16 @@ function getNpmInstallStringMappings (save, saveDev, saveExact, force) {
106126
.set('force', force ? '--force' : undefined);
107127
}
108128

129+
/**
130+
* Gets a map of arguments where the value is the corresponding yarn strings
131+
*/
132+
function getYarnAddStringMappings (dev, exact, force) {
133+
return new Map()
134+
.set('dev', dev ? '--dev' : undefined)
135+
.set('exact', exact ? '--exact' : undefined)
136+
.set('force', force ? '--force' : undefined);
137+
}
138+
109139
/**
110140
* Gets the prompter from an adapter given an adapter path
111141
*/
@@ -115,7 +145,7 @@ function getPrompter (adapterPath) {
115145

116146
// Load the adapter
117147
let adapter = require(resolvedAdapterPath);
118-
148+
119149
/* istanbul ignore next */
120150
if (adapter && adapter.prompter && isFunction(adapter.prompter)) {
121151
return adapter.prompter;

src/commitizen/init.js

Lines changed: 41 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ import * as adapter from './adapter';
66
let {
77
addPathToAdapterConfig,
88
generateNpmInstallAdapterCommand,
9-
getNpmInstallStringMappings
9+
getNpmInstallStringMappings,
10+
generateYarnAddAdapterCommand,
11+
getYarnAddStringMappings,
1012
} = adapter;
1113

1214
export default init;
@@ -15,12 +17,12 @@ const CLI_PATH = path.normalize(__dirname + '/../../');
1517

1618
/**
1719
* CZ INIT
18-
*
20+
*
1921
* Init is generally responsible for initializing an adapter in
20-
* a user's project. The goal is to be able to run
22+
* a user's project. The goal is to be able to run
2123
* `commitizen init` and be prompted for certain fields which
2224
* will help you install the proper adapter for your project.
23-
*
25+
*
2426
* Init does not actually create the adapter (it defers to adapter
2527
* for this). Instead, it is specifically designed to help gather
2628
* and validate the information needed to install the adapter
@@ -34,65 +36,74 @@ const defaultInitOptions = {
3436
save: false,
3537
saveDev: true,
3638
saveExact: false,
37-
force: false
39+
force: false,
40+
41+
// for --yarn use
42+
// @see https://github.com/commitizen/cz-cli/issues/527#issuecomment-392653897
43+
yarn: false,
44+
dev: true,
45+
exact: false, // should add trailing comma, thus next developer doesn't got blamed for this line
3846
};
3947

4048
/**
4149
* Runs npm install for the adapter then modifies the config.commitizen as needed
4250
*/
4351
function init (sh, repoPath, adapterNpmName, {
44-
save = false,
45-
saveDev = true,
52+
save = false,
53+
saveDev = true,
4654
saveExact = false,
47-
force = false
55+
force = false,
56+
yarn = false,
57+
dev = false,
58+
exact = false,
4859
} = defaultInitOptions) {
49-
60+
5061
// Don't let things move forward if required args are missing
5162
checkRequiredArguments(sh, repoPath, adapterNpmName);
52-
63+
5364
// Move to the correct directory so we can run commands
5465
sh.cd(repoPath);
55-
66+
5667
// Load the current adapter config
5768
let adapterConfig = loadAdapterConfig();
58-
69+
5970
// Get the npm string mappings based on the arguments provided
60-
let stringMappings = getNpmInstallStringMappings(save, saveDev, saveExact, force);
61-
71+
let stringMappings = yarn ? getYarnAddStringMappings(dev, exact, force) : getNpmInstallStringMappings(save, saveDev, saveExact, force);
72+
6273
// Generate a string that represents the npm install command
63-
let installAdapterCommand = generateNpmInstallAdapterCommand(stringMappings, adapterNpmName);
74+
let installAdapterCommand = yarn ? generateYarnAddAdapterCommand(stringMappings, adapterNpmName) : generateNpmInstallAdapterCommand(stringMappings, adapterNpmName);
6475

6576
// Check for previously installed adapters
6677
if (adapterConfig && adapterConfig.path && adapterConfig.path.length > 0) {
67-
78+
6879
// console.log(`
69-
// Previous adapter detected!
80+
// Previous adapter detected!
7081
// `);
71-
72-
if (!force) {
73-
82+
83+
if (!force) {
84+
7485
// console.log(`
75-
// Previous adapter detected!
86+
// Previous adapter detected!
7687
// `);
77-
78-
throw 'A previous adapter is already configured. Use --force to override';
88+
89+
throw 'A previous adapter is already configured. Use --force to override';
7990
} else { // Override it
8091
try {
8192
executeShellCommand(sh, repoPath, installAdapterCommand);
82-
addPathToAdapterConfig(sh, CLI_PATH, repoPath, adapterNpmName);
93+
addPathToAdapterConfig(sh, CLI_PATH, repoPath, adapterNpmName);
8394
} catch (e) {
8495
console.error(e);
8596
}
8697
}
87-
98+
8899
} else {
89-
100+
90101
// console.log(`
91-
// No previous adapter was detected
92-
// `);
102+
// No previous adapter was detected
103+
// `);
93104

94105
try {
95-
106+
96107
executeShellCommand(sh, repoPath, installAdapterCommand);
97108
addPathToAdapterConfig(sh, CLI_PATH, repoPath, adapterNpmName);
98109
} catch (e) {
@@ -124,7 +135,7 @@ function checkRequiredArguments (sh, path, adapterNpmName) {
124135
function loadAdapterConfig () {
125136
let config = configLoader.load();
126137
if (config) {
127-
return config;
138+
return config;
128139
} else {
129140
return;
130141
}

test/helpers/register.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
require('babel-core/register')({
2+
ignore: [
3+
/node_modules/,
4+
/yarn/
5+
]
6+
});

test/tests/init.js

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,139 @@ describe('init', function () {
158158
expect(semver.inc(range, 'major')).not.to.equal(null);
159159

160160
});
161+
162+
it('installs an adapter with --yarn', function () {
163+
164+
this.timeout(config.maxTimeout); // this could take a while
165+
166+
// SETUP
167+
168+
// Install an adapter
169+
commitizenInit(sh, config.paths.endUserRepo, 'cz-conventional-changelog', { yarn: true });
170+
171+
// TEST
172+
173+
// Check resulting json
174+
let packageJson = util.getParsedPackageJsonFromPath(config.paths.endUserRepo);
175+
expect(packageJson).to.have.deep.property('dependencies.cz-conventional-changelog');
176+
177+
});
178+
179+
180+
it('installs an adapter with --yarn --dev', function () {
181+
182+
this.timeout(config.maxTimeout); // this could take a while
183+
184+
// SETUP
185+
186+
// Install an adapter
187+
commitizenInit(sh, config.paths.endUserRepo, 'cz-conventional-changelog', { yarn: true, dev: true });
188+
189+
// TEST
190+
191+
// Check resulting json
192+
let packageJson = util.getParsedPackageJsonFromPath(config.paths.endUserRepo);
193+
expect(packageJson).to.have.deep.property('devDependencies.cz-conventional-changelog');
194+
195+
});
196+
197+
it('errors (with --yarn) on previously installed adapter', function () {
198+
199+
this.timeout(config.maxTimeout); // this could take a while
200+
201+
// SETUP
202+
203+
// Add a first adapter
204+
sh.cd(config.paths.endUserRepo);
205+
commitizenInit(sh, config.paths.endUserRepo, 'cz-conventional-changelog', { yarn: true, dev: true });
206+
207+
// TEST
208+
sh.cd(config.paths.endUserRepo);
209+
// Adding a second adapter
210+
expect(function () {
211+
commitizenInit(sh, config.paths.endUserRepo, 'cz-jira-smart-commit', { yarn: true, dev: true });
212+
}).to.throw(/already configured/);
213+
214+
// Check resulting json
215+
let packageJson = util.getParsedPackageJsonFromPath(config.paths.endUserRepo);
216+
expect(packageJson).not.to.have.deep.property('devDependencies', 'cz-jira-smart-commit');
217+
expect(packageJson).to.have.deep.property('config.commitizen.path', './node_modules/cz-conventional-changelog');
218+
// TODO: Eventually may need to offer both path and package keys. package = npm package name
219+
// Path for local development
220+
});
221+
222+
it('succeeds (with --yarn) if force is true', function () {
223+
224+
this.timeout(config.maxTimeout); // this could take a while
225+
226+
// SETUP
227+
228+
// Add a first adapter
229+
sh.cd(config.paths.endUserRepo);
230+
commitizenInit(sh, config.paths.endUserRepo, 'cz-conventional-changelog', { yarn: true, dev: true });
231+
232+
// TEST
233+
234+
// Adding a second adapter
235+
expect(function () {
236+
commitizenInit(sh, config.paths.endUserRepo, 'cz-jira-smart-commit', { yarn: true, dev: true, force: true });
237+
}).to.not.throw();
238+
239+
let packageJson = util.getParsedPackageJsonFromPath(config.paths.endUserRepo);
240+
expect(packageJson.devDependencies).to.have.property('cz-jira-smart-commit');
241+
expect(packageJson).to.have.deep.property('config.commitizen.path', './node_modules/cz-jira-smart-commit');
242+
243+
});
244+
245+
it('installs (with --yarn) an adapter without --save-exact', function () {
246+
247+
this.timeout(config.maxTimeout); // this could take a while
248+
249+
// SETUP
250+
251+
// Add a first adapter
252+
sh.cd(config.paths.endUserRepo);
253+
commitizenInit(sh, config.paths.endUserRepo, 'cz-conventional-changelog', { yarn: true, dev: true });
254+
let packageJson = util.getParsedPackageJsonFromPath(config.paths.endUserRepo);
255+
256+
// TEST
257+
expect(packageJson.devDependencies).to.have.property('cz-conventional-changelog');
258+
let range = packageJson.devDependencies['cz-conventional-changelog'];
259+
260+
// It should satisfy the requirements of a range
261+
expect(semver.validRange(range)).to.not.equal(null);
262+
263+
// // But you CAN NOT increment a range
264+
// expect(semver.inc(range, 'major')).to.equal(null);
265+
// TODO: We need to figure out how to check if the repo has save exact set
266+
// in the config before we can re-enable this. The --save-exact setting
267+
// in our package.json breaks this test
268+
269+
});
270+
271+
it('installs an adapter with --yarn --exact', function () {
272+
273+
this.timeout(config.maxTimeout); // this could take a while
274+
275+
// SETUP
276+
277+
// Add a first adapter
278+
sh.cd(config.paths.endUserRepo);
279+
commitizenInit(sh, config.paths.endUserRepo, 'cz-conventional-changelog', { yarn: true, dev: true, exact: true });
280+
let packageJson = util.getParsedPackageJsonFromPath(config.paths.endUserRepo);
281+
282+
// TEST
283+
expect(packageJson.devDependencies).to.have.property('cz-conventional-changelog');
284+
let range = packageJson.devDependencies['cz-conventional-changelog'];
285+
286+
// It should satisfy the requirements of a range
287+
expect(semver.validRange(range)).to.not.equal(null);
288+
289+
// But you CAN increment a single version
290+
expect(semver.inc(range, 'major')).not.to.equal(null);
291+
292+
});
293+
161294
});
162295

163296
afterEach(function () {

0 commit comments

Comments
 (0)