Skip to content

Commit 58d492e

Browse files
marionebljimthedev
authored andcommitted
feat(adapter): support npm module names in commitizen.path config
This change contains * A refactoring of the `adapter.resolveAdapterPath` method * An additional test case for the `adapter.resolveAdapterPath` method * Updated documentation to reflect the changes * Remove unneeded resolving of param passed to getPrompter in git-cz strategy * Use find-root package to determine position of package.json to determine the path more reliable in complex npm situations Instead of doing `fs.lsStatSync` calls against `commitizen.path` `resolveAdapterPath` relies on `require.resolve`, which allows for support of npm module names in `commitizen.path` while maintaining backwards compatibility with the former implementation and documentation. Closes #79
1 parent d94487f commit 58d492e

File tree

5 files changed

+54
-58
lines changed

5 files changed

+54
-58
lines changed

README.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,21 @@ The above command does three things for you. It installs the cz-conventional-cha
4747
...
4848
"config": {
4949
"commitizen": {
50-
"path": "node_modules/cz-conventional-changelog"
50+
"path": "cz-conventional-changelog"
5151
}
5252
}
5353
```
5454

5555
This just tells Commitizen which adapter we actually want our contributors to use when they try to commit to this repo.
5656

57+
`commitizen.path` is resolved via [require.resolve](https://nodejs.org/api/globals.html#globals_require_resolve) and supports
58+
59+
* npm modules
60+
* directories relative to `process.cwd()` containing an `index.js` file
61+
* file base names relative to `process.cwd()` with `js` extension
62+
* full relative file names
63+
* absolute paths.
64+
5765
Please note that in previous version of Commitizen we used czConfig. **czConfig has been deprecated** and you should migrate to the new format before Commitizen 3.0.0.
5866

5967
#### Congratulations your repo is Commitizen-friendly. Time to flaunt it!

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
"glob": "7.0.0",
7171
"inquirer": "0.12.0",
7272
"lodash": "4.6.1",
73+
"find-root": "^1.0.0",
7374
"minimist": "1.2.0",
7475
"shelljs": "0.5.3",
7576
"strip-json-comments": "2.0.1"

src/cli/strategies/git-cz.js

Lines changed: 22 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import fs from 'fs';
22
import path from 'path';
33
import sh from 'shelljs';
44
import inquirer from 'inquirer';
5+
import findRoot from 'find-root';
56
import {getParsedPackageJsonFromPath} from '../../common/util';
67
import {gitCz as gitCzParser, commitizen as commitizenParser} from '../parsers';
78
import {commit, staging, adapter} from '../../commitizen';
@@ -11,8 +12,8 @@ import * as gitStrategy from './git';
1112
// destructure for shorter apis
1213
let { parse } = gitCzParser;
1314

14-
let { getNearestNodeModulesDirectory, getNearestProjectRootDirectory, getPrompter } = adapter;
15-
let { isClean } = staging;
15+
let { getPrompter, resolveAdapterPath } = adapter;
16+
let { isClean } = staging;
1617

1718
export default gitCz;
1819

@@ -37,31 +38,24 @@ function gitCz(rawGitArgs, environment, adapterConfig) {
3738

3839
// Now, if we've made it past overrides, proceed with the git-cz strategy
3940
let parsedGitCzArgs = parse(rawGitArgs);
40-
41-
// TODO: This can be broken out into its own function.
42-
// Basically we're
43-
// 1. Walking up the tree to find a node_modules folder
44-
// 2. Resolving the project root based on the node_modules folder
45-
// 3. Resolving the adapter bath based on that project root
46-
let resolvedAdapterConfigPath = path.join(getNearestProjectRootDirectory(), adapterConfig.path);
47-
48-
let prompter = getPrompter(path.resolve(process.cwd(), resolvedAdapterConfigPath));
41+
let resolvedAdapterConfigPath = resolveAdapterPath(adapterConfig.path);
42+
let resolvedAdapterRootPath = findRoot(resolvedAdapterConfigPath);
43+
let prompter = getPrompter(adapterConfig.path);
4944

50-
isClean(process.cwd(), function(stagingIsClean){
51-
if(stagingIsClean) {
52-
console.error('Error: No files added to staging! Did you forget to run git add?')
53-
} else {
54-
55-
// OH GOD IM SORRY FOR THIS SECTION
56-
let adapterPackageJson = getParsedPackageJsonFromPath(resolvedAdapterConfigPath);
57-
let cliPackageJson = getParsedPackageJsonFromPath(environment.cliPath);
58-
console.log(`cz-cli@${cliPackageJson.version}, ${adapterPackageJson.name}@${adapterPackageJson.version}\n`);
59-
commit(sh, inquirer, process.cwd(), prompter, {args: parsedGitCzArgs, disableAppendPaths:true, emitData:true, quiet:false}, function() {
60-
// console.log('commit happened');
61-
});
62-
63-
}
64-
});
65-
45+
isClean(process.cwd(), function(stagingIsClean){
46+
if(stagingIsClean) {
47+
console.error('Error: No files added to staging! Did you forget to run git add?')
48+
} else {
49+
// OH GOD IM SORRY FOR THIS SECTION
50+
let adapterPackageJson = getParsedPackageJsonFromPath(resolvedAdapterRootPath);
51+
let cliPackageJson = getParsedPackageJsonFromPath(environment.cliPath);
52+
console.log(`cz-cli@${cliPackageJson.version}, ${adapterPackageJson.name}@${adapterPackageJson.version}\n`);
53+
commit(sh, inquirer, process.cwd(), prompter, {args: parsedGitCzArgs, disableAppendPaths:true, emitData:true, quiet:false}, function() {
54+
// console.log('commit happened');
55+
});
56+
57+
}
58+
});
59+
6660

67-
}
61+
}

src/commitizen/adapter.js

Lines changed: 20 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@ export {
1818

1919
/**
2020
* ADAPTER
21-
*
22-
* Adapter is generally responsible for actually installing adapters to an
21+
*
22+
* Adapter is generally responsible for actually installing adapters to an
2323
* end user's project. It does not perform checks to determine if there is
2424
* a previous commitizen adapter installed or if the proper fields were
25-
* provided. It defers that responsibility to init.
25+
* provided. It defers that responsibility to init.
2626
*/
2727

2828
/**
@@ -107,44 +107,38 @@ function getNpmInstallStringMappings(save, saveDev, saveExact, force) {
107107
* Gets the prompter from an adapter given an adapter path
108108
*/
109109
function getPrompter(adapterPath) {
110-
// We need to handle directories and files, so resolve the parh first
110+
// Resolve the adapter path
111111
let resolvedAdapterPath = resolveAdapterPath(adapterPath);
112112

113113
// Load the adapter
114114
let adapter = require(resolvedAdapterPath);
115115

116116
if(adapter && adapter.prompter && isFunction(adapter.prompter)) {
117-
return adapter.prompter;
117+
return adapter.prompter;
118118
} else {
119119
throw "Could not find prompter method in the provided adapter module: " + adapterPath;
120120
}
121121
}
122122

123123
/**
124-
* Given a path, which can be a directory or file, will
124+
* Given a resolvable module name or path, which can be a directory or file, will
125125
* return a located adapter path or will throw.
126126
*/
127127
function resolveAdapterPath(inboundAdapterPath) {
128-
let outboundAdapterPath;
128+
// Check if inboundAdapterPath is a path or node module name
129+
let parsed = path.parse(inboundAdapterPath);
130+
let isPath = parsed.dir.length > 0;
131+
132+
// Resolve from process.cwd() if inboundAdapterPath is a path
133+
let absoluteAdapterPath = isPath ?
134+
path.resolve(process.cwd(), inboundAdapterPath) :
135+
inboundAdapterPath;
129136

130-
// Try to open the provided path
131137
try {
132-
133-
// If we're given a directory, append index.js
134-
if(fs.lstatSync(inboundAdapterPath).isDirectory()) {
135-
136-
// Modify the path and make sure the modified path exists
137-
outboundAdapterPath = path.join(inboundAdapterPath, 'index.js');
138-
fs.lstatSync(outboundAdapterPath);
139-
140-
} else {
141-
// The file exists and is a file, so just return it
142-
outboundAdapterPath = inboundAdapterPath;
143-
}
144-
return outboundAdapterPath;
145-
146-
} catch(err) {
147-
throw err;
138+
// try to resolve the given path
139+
return require.resolve(absoluteAdapterPath);
140+
} catch (error) {
141+
error.message = "Could not resolve " + absoluteAdapterPath, ". " + error.message;
142+
throw error;
148143
}
149-
150-
}
144+
}

test/tests/adapter.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import {expect} from 'chai';
22
import path from 'path';
3-
import fs from 'fs';
43

54
// TODO: augment these tests with tests using the actual cli call
65
// For now we're just using the library, which is probably fine
@@ -60,8 +59,8 @@ describe('adapter', function() {
6059
// TEST
6160
expect(function() {adapter.resolveAdapterPath('IAMANIMPOSSIBLEPATH'); }).to.throw(Error);
6261
expect(function() {adapter.resolveAdapterPath(adapterConfig.path); }).not.to.throw(Error);
63-
expect(function() {adapter.resolveAdapterPath(path.join(adapterConfig.path, 'index.js')) }).not.to.throw(Error);
64-
62+
expect(function() {adapter.resolveAdapterPath(path.join(adapterConfig.path, 'index.js')); }).not.to.throw(Error);
63+
expect(function() {adapter.resolveAdapterPath('cz-conventional-changelog'); }).not.to.throw(Error);
6564
});
6665

6766
it('gets adapter prompter functions', function(){

0 commit comments

Comments
 (0)