Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
feat: support initialization with yarn. fixes #527 (#549)
Support `commitizen init` with `yarn` by adding `--yarn` flag. 
See discussion here #527.

fixes #527
  • Loading branch information
SevenOutman authored and jimthedev committed Sep 18, 2018
1 parent a70c234 commit d565215
Show file tree
Hide file tree
Showing 6 changed files with 220 additions and 34 deletions.
8 changes: 7 additions & 1 deletion README.md
Expand Up @@ -79,9 +79,15 @@ Next, initialize your project to use the cz-conventional-changelog adapter by ty
commitizen init cz-conventional-changelog --save-dev --save-exact
```

Or if you are using Yarn:

```
commitizen init cz-conventional-changelog --yarn --dev --exact
```

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`.

The above command does three things for you.
The above command does three things for you.
1. Installs the cz-conventional-changelog adapter npm module
2. Saves it to package.json's dependencies or devDependencies
3. Adds the `config.commitizen` key to the root of your **package.json** as shown here:
Expand Down
2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -14,7 +14,7 @@
"semantic-release": "semantic-release pre && npm publish && semantic-release post",
"start": "npm run test:watch",
"lint": "eslint --ignore-path .gitignore .",
"test": "nyc --require babel-core/register _mocha -- test/tests/index.js",
"test": "nyc --require ./test/helpers/register _mocha -- test/tests/index.js",
"test:watch": "nodemon -q --ignore test/.tmp/ --ignore test/artifacts/ --ignore coverage/ --exec \"npm run test\" --",
"test:windows": "node ./test/tools/trigger-appveyor-tests.js"
},
Expand Down
34 changes: 32 additions & 2 deletions src/commitizen/adapter.js
Expand Up @@ -14,7 +14,9 @@ export {
getNpmInstallStringMappings,
getPrompter,
generateNpmInstallAdapterCommand,
resolveAdapterPath
resolveAdapterPath,
getYarnAddStringMappings,
generateYarnAddAdapterCommand,
};

/**
Expand Down Expand Up @@ -70,6 +72,24 @@ function generateNpmInstallAdapterCommand (stringMappings, adapterNpmName) {
return installAdapterCommand;
}

/**
* Generates an yarn add command given a map of strings and a package name
*/
function generateYarnAddAdapterCommand (stringMappings, adapterNpmName) {

// Start with an initial yarn add command
let installAdapterCommand = `yarn add ${adapterNpmName}`;

// Append the necessary arguments to it based on user preferences
for (let [key, value] of stringMappings.entries()) {
if (value) {
installAdapterCommand = installAdapterCommand + ' ' + value;
}
}

return installAdapterCommand;
}

/**
* Gets the nearest npm_modules directory
*/
Expand Down Expand Up @@ -106,6 +126,16 @@ function getNpmInstallStringMappings (save, saveDev, saveExact, force) {
.set('force', force ? '--force' : undefined);
}

/**
* Gets a map of arguments where the value is the corresponding yarn strings
*/
function getYarnAddStringMappings (dev, exact, force) {
return new Map()
.set('dev', dev ? '--dev' : undefined)
.set('exact', exact ? '--exact' : undefined)
.set('force', force ? '--force' : undefined);
}

/**
* Gets the prompter from an adapter given an adapter path
*/
Expand All @@ -115,7 +145,7 @@ function getPrompter (adapterPath) {

// Load the adapter
let adapter = require(resolvedAdapterPath);

/* istanbul ignore next */
if (adapter && adapter.prompter && isFunction(adapter.prompter)) {
return adapter.prompter;
Expand Down
71 changes: 41 additions & 30 deletions src/commitizen/init.js
Expand Up @@ -6,7 +6,9 @@ import * as adapter from './adapter';
let {
addPathToAdapterConfig,
generateNpmInstallAdapterCommand,
getNpmInstallStringMappings
getNpmInstallStringMappings,
generateYarnAddAdapterCommand,
getYarnAddStringMappings,
} = adapter;

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

/**
* CZ INIT
*
*
* Init is generally responsible for initializing an adapter in
* a user's project. The goal is to be able to run
* a user's project. The goal is to be able to run
* `commitizen init` and be prompted for certain fields which
* will help you install the proper adapter for your project.
*
*
* Init does not actually create the adapter (it defers to adapter
* for this). Instead, it is specifically designed to help gather
* and validate the information needed to install the adapter
Expand All @@ -34,65 +36,74 @@ const defaultInitOptions = {
save: false,
saveDev: true,
saveExact: false,
force: false
force: false,

// for --yarn use
// @see https://github.com/commitizen/cz-cli/issues/527#issuecomment-392653897
yarn: false,
dev: true,
exact: false, // should add trailing comma, thus next developer doesn't got blamed for this line
};

/**
* Runs npm install for the adapter then modifies the config.commitizen as needed
*/
function init (sh, repoPath, adapterNpmName, {
save = false,
saveDev = true,
save = false,
saveDev = true,
saveExact = false,
force = false
force = false,
yarn = false,
dev = false,
exact = false,
} = defaultInitOptions) {

// Don't let things move forward if required args are missing
checkRequiredArguments(sh, repoPath, adapterNpmName);

// Move to the correct directory so we can run commands
sh.cd(repoPath);

// Load the current adapter config
let adapterConfig = loadAdapterConfig();

// Get the npm string mappings based on the arguments provided
let stringMappings = getNpmInstallStringMappings(save, saveDev, saveExact, force);
let stringMappings = yarn ? getYarnAddStringMappings(dev, exact, force) : getNpmInstallStringMappings(save, saveDev, saveExact, force);

// Generate a string that represents the npm install command
let installAdapterCommand = generateNpmInstallAdapterCommand(stringMappings, adapterNpmName);
let installAdapterCommand = yarn ? generateYarnAddAdapterCommand(stringMappings, adapterNpmName) : generateNpmInstallAdapterCommand(stringMappings, adapterNpmName);

// Check for previously installed adapters
if (adapterConfig && adapterConfig.path && adapterConfig.path.length > 0) {

// console.log(`
// Previous adapter detected!
// Previous adapter detected!
// `);
if (!force) {

if (!force) {

// console.log(`
// Previous adapter detected!
// Previous adapter detected!
// `);
throw 'A previous adapter is already configured. Use --force to override';

throw 'A previous adapter is already configured. Use --force to override';
} else { // Override it
try {
executeShellCommand(sh, repoPath, installAdapterCommand);
addPathToAdapterConfig(sh, CLI_PATH, repoPath, adapterNpmName);
addPathToAdapterConfig(sh, CLI_PATH, repoPath, adapterNpmName);
} catch (e) {
console.error(e);
}
}

} else {

// console.log(`
// No previous adapter was detected
// `);
// No previous adapter was detected
// `);

try {

executeShellCommand(sh, repoPath, installAdapterCommand);
addPathToAdapterConfig(sh, CLI_PATH, repoPath, adapterNpmName);
} catch (e) {
Expand Down Expand Up @@ -124,7 +135,7 @@ function checkRequiredArguments (sh, path, adapterNpmName) {
function loadAdapterConfig () {
let config = configLoader.load();
if (config) {
return config;
return config;
} else {
return;
}
Expand Down
6 changes: 6 additions & 0 deletions test/helpers/register.js
@@ -0,0 +1,6 @@
require('babel-core/register')({
ignore: [
/node_modules/,
/yarn/
]
});
133 changes: 133 additions & 0 deletions test/tests/init.js
Expand Up @@ -158,6 +158,139 @@ describe('init', function () {
expect(semver.inc(range, 'major')).not.to.equal(null);

});

it('installs an adapter with --yarn', function () {

this.timeout(config.maxTimeout); // this could take a while

// SETUP

// Install an adapter
commitizenInit(sh, config.paths.endUserRepo, 'cz-conventional-changelog', { yarn: true });

// TEST

// Check resulting json
let packageJson = util.getParsedPackageJsonFromPath(config.paths.endUserRepo);
expect(packageJson).to.have.deep.property('dependencies.cz-conventional-changelog');

});


it('installs an adapter with --yarn --dev', function () {

this.timeout(config.maxTimeout); // this could take a while

// SETUP

// Install an adapter
commitizenInit(sh, config.paths.endUserRepo, 'cz-conventional-changelog', { yarn: true, dev: true });

// TEST

// Check resulting json
let packageJson = util.getParsedPackageJsonFromPath(config.paths.endUserRepo);
expect(packageJson).to.have.deep.property('devDependencies.cz-conventional-changelog');

});

it('errors (with --yarn) on previously installed adapter', function () {

this.timeout(config.maxTimeout); // this could take a while

// SETUP

// Add a first adapter
sh.cd(config.paths.endUserRepo);
commitizenInit(sh, config.paths.endUserRepo, 'cz-conventional-changelog', { yarn: true, dev: true });

// TEST
sh.cd(config.paths.endUserRepo);
// Adding a second adapter
expect(function () {
commitizenInit(sh, config.paths.endUserRepo, 'cz-jira-smart-commit', { yarn: true, dev: true });
}).to.throw(/already configured/);

// Check resulting json
let packageJson = util.getParsedPackageJsonFromPath(config.paths.endUserRepo);
expect(packageJson).not.to.have.deep.property('devDependencies', 'cz-jira-smart-commit');
expect(packageJson).to.have.deep.property('config.commitizen.path', './node_modules/cz-conventional-changelog');
// TODO: Eventually may need to offer both path and package keys. package = npm package name
// Path for local development
});

it('succeeds (with --yarn) if force is true', function () {

this.timeout(config.maxTimeout); // this could take a while

// SETUP

// Add a first adapter
sh.cd(config.paths.endUserRepo);
commitizenInit(sh, config.paths.endUserRepo, 'cz-conventional-changelog', { yarn: true, dev: true });

// TEST

// Adding a second adapter
expect(function () {
commitizenInit(sh, config.paths.endUserRepo, 'cz-jira-smart-commit', { yarn: true, dev: true, force: true });
}).to.not.throw();

let packageJson = util.getParsedPackageJsonFromPath(config.paths.endUserRepo);
expect(packageJson.devDependencies).to.have.property('cz-jira-smart-commit');
expect(packageJson).to.have.deep.property('config.commitizen.path', './node_modules/cz-jira-smart-commit');

});

it('installs (with --yarn) an adapter without --save-exact', function () {

this.timeout(config.maxTimeout); // this could take a while

// SETUP

// Add a first adapter
sh.cd(config.paths.endUserRepo);
commitizenInit(sh, config.paths.endUserRepo, 'cz-conventional-changelog', { yarn: true, dev: true });
let packageJson = util.getParsedPackageJsonFromPath(config.paths.endUserRepo);

// TEST
expect(packageJson.devDependencies).to.have.property('cz-conventional-changelog');
let range = packageJson.devDependencies['cz-conventional-changelog'];

// It should satisfy the requirements of a range
expect(semver.validRange(range)).to.not.equal(null);

// // But you CAN NOT increment a range
// expect(semver.inc(range, 'major')).to.equal(null);
// TODO: We need to figure out how to check if the repo has save exact set
// in the config before we can re-enable this. The --save-exact setting
// in our package.json breaks this test

});

it('installs an adapter with --yarn --exact', function () {

this.timeout(config.maxTimeout); // this could take a while

// SETUP

// Add a first adapter
sh.cd(config.paths.endUserRepo);
commitizenInit(sh, config.paths.endUserRepo, 'cz-conventional-changelog', { yarn: true, dev: true, exact: true });
let packageJson = util.getParsedPackageJsonFromPath(config.paths.endUserRepo);

// TEST
expect(packageJson.devDependencies).to.have.property('cz-conventional-changelog');
let range = packageJson.devDependencies['cz-conventional-changelog'];

// It should satisfy the requirements of a range
expect(semver.validRange(range)).to.not.equal(null);

// But you CAN increment a single version
expect(semver.inc(range, 'major')).not.to.equal(null);

});

});

afterEach(function () {
Expand Down

0 comments on commit d565215

Please sign in to comment.