Skip to content

Commit

Permalink
Merge branch 'master' into fix-usePreparedCommit
Browse files Browse the repository at this point in the history
  • Loading branch information
leonardoanalista committed Sep 2, 2022
2 parents d9d2322 + 9b82ca8 commit bfe9214
Show file tree
Hide file tree
Showing 15 changed files with 270 additions and 431 deletions.
71 changes: 71 additions & 0 deletions .cz-config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
console.info('>>> local config in repo: ', __dirname);

module.exports = {
types: [
{ value: 'feat', name: 'feat: A new feature' },
{ value: 'fix', name: 'fix: A bug fix' },
{ value: 'docs', name: 'docs: Documentation only changes' },
{
value: 'style',
name: 'style: Changes that do not affect the meaning of the code\n (white-space, formatting, missing semi-colons, etc)',
},
{
value: 'refactor',
name: 'refactor: A code change that neither fixes a bug nor adds a feature',
},
{
value: 'perf',
name: 'perf: A code change that improves performance',
},
{ value: 'test', name: 'test: Adding missing tests' },
{
value: 'chore',
name: 'chore: Changes to the build process or auxiliary tools\n and libraries such as documentation generation',
},
{ value: 'revert', name: 'revert: Revert to a commit' },
{ value: 'WIP', name: 'WIP: Work in progress' },
],

scopes: [{ name: 'accounts' }, { name: 'admin' }, { name: 'exampleScope' }, { name: 'changeMe' }],

allowTicketNumber: false,
isTicketNumberRequired: false,
ticketNumberPrefix: 'TICKET-',
ticketNumberRegExp: '\\d{1,5}',

// it needs to match the value for field type. Eg.: 'fix'
/*
scopeOverrides: {
fix: [
{name: 'merge'},
{name: 'style'},
{name: 'e2eTest'},
{name: 'unitTest'}
]
},
*/
// override the messages, defaults are as follows
messages: {
type: "Select the type of change that you're committing:",
scope: '\nDenote the SCOPE of this change (optional):',
// used if allowCustomScopes is true
customScope: 'Denote the SCOPE of this change:',
subject: 'Write a SHORT, IMPERATIVE tense description of the change:\n',
body: 'Provide a LONGER description of the change (optional). Use "|" to break new line:\n',
breaking: 'List any BREAKING CHANGES (optional):\n',
footer: 'List any ISSUES CLOSED by this change (optional). E.g.: #31, #34:\n',
confirmCommit: 'Are you sure you want to proceed with the commit above?',
},

allowCustomScopes: true,
allowBreakingChanges: ['feat', 'fix'],
// skip any questions you want
skipQuestions: ['body'],

// limit subject length
subjectLimit: 100,
// breaklineChar: '|', // It is supported for fields body and footer.
// footerPrefix : 'ISSUES CLOSED:'
// askForBreakingChangeFirst : true, // default is false
};
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,4 @@ build/Release
node_modules
.DS_Store
coverage
.cz-config.js
!.cz-config.js
2 changes: 1 addition & 1 deletion .prettierrc
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"printWidth": 120,
"singleQuote": true,
"trailingComma": "es5"
"trailingComma": "all"
}
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# cz-customizable

The customizable Commitizen plugin (or standalone utility) to help achieve consistent commit messages like the [AngularJS team](https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md#-git-commit-guidelines).
The customizable Commitizen plugin (or standalone utility) to help achieve consistent commit messages like the [AngularJS team](https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md#-git-commit-guidelines). Note that you can create any commit message pattern. You don't have to use the pattern from the Angular team. For example, my team uses this pattern: `[minor] add new feature x`

![screenshot](screenshot.png)

Expand Down
7 changes: 7 additions & 0 deletions __tests__/build-commit.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,4 +98,11 @@ line 2`;
expect(buildCommit(answersNoScope, options)).toEqual(expecteMessage);
});
});

it('should escape harmful characters', () => {
const altAnswers = { ...answers, subject: 'th"is i\'s a n`ew $ f<ea>ture &' };

// eslint-disable-next-line prettier/prettier, no-useless-escape
expect(buildCommit(altAnswers, {})).toEqual('feat(app): th\\\"is i\'s a n\\`ew \\\\$ f\\<ea\\>ture \\&');
});
});
12 changes: 6 additions & 6 deletions __tests__/cz-customizable.test.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
const czModule = require('../index');
const readConfigFile = require('../lib/read-config');
const readConfigFile = require('../lib/read-config-file');
const getPreviousCommit = require('../lib/utils/get-previous-commit');

const commit = jest.fn();

jest.mock('./../lib/read-config');
jest.mock('./../lib/read-config-file');
jest.mock('./../lib/utils/get-previous-commit');

beforeEach(() => {
Expand Down Expand Up @@ -125,7 +125,7 @@ describe('cz-customizable', () => {
czModule.prompter(mockCz, commit);

expect(commit).toHaveBeenCalledWith(
'feat(myScope): create a new cool feature\n\n-line1\n-line2\n\nBREAKING CHANGE:\nbreaking\n\nISSUES CLOSED: my footer'
'feat(myScope): create a new cool feature\n\n-line1\n-line2\n\nBREAKING CHANGE:\nbreaking\n\nISSUES CLOSED: my footer',
);
});

Expand Down Expand Up @@ -248,7 +248,7 @@ describe('cz-customizable', () => {
czModule.prompter(mockCz, commit);

expect(commit).toHaveBeenCalledWith(
'feat(myScope): create a new cool feature\n\nWARNING:\nbreaking\n\nISSUES CLOSED: my footer'
'feat(myScope): create a new cool feature\n\nWARNING:\nbreaking\n\nISSUES CLOSED: my footer',
);
});

Expand Down Expand Up @@ -277,7 +277,7 @@ describe('cz-customizable', () => {
czModule.prompter(mockCz, commit);

expect(commit).toHaveBeenCalledWith(
'feat(myScope): create a new cool feature\n\nBREAKING CHANGE:\nbreaking\n\nFIXES: my footer'
'feat(myScope): create a new cool feature\n\nBREAKING CHANGE:\nbreaking\n\nFIXES: my footer',
);
});

Expand Down Expand Up @@ -306,7 +306,7 @@ describe('cz-customizable', () => {
czModule.prompter(mockCz, commit);

expect(commit).toHaveBeenCalledWith(
'feat(myScope): create a new cool feature\n\nBREAKING CHANGE:\nbreaking\n\nmy footer'
'feat(myScope): create a new cool feature\n\nBREAKING CHANGE:\nbreaking\n\nmy footer',
);
});

Expand Down
4 changes: 2 additions & 2 deletions __tests__/questions.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,8 @@ describe('cz-customizable', () => {
expect(getQuestion(5).validate('good subject')).toEqual(true);
expect(
getQuestion(5).validate(
'bad subject that exceed limit bad subject that exceed limitbad subject that exceed limit test test test'
)
'bad subject that exceed limit bad subject that exceed limitbad subject that exceed limit test test test',
),
).toEqual('Exceed limit: 100');
});

Expand Down
17 changes: 17 additions & 0 deletions __tests__/read-config-no-config.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
const readConfigFile = require('../lib/read-config-file');
const log = require('../lib/logger');

jest.mock('find-config');
jest.mock('../lib/logger', () => ({
error: jest.fn(),
}));

// This is a unit test but it reads real a config file in te project root.
// It could be called "integration". The most important is it increase our confidence.
it('logs message when config is not found', () => {
const config = readConfigFile();
expect(config).toEqual(null);
expect(log.error).toHaveBeenCalledWith(
'Unable to find a configuration file. Please refer to documentation to learn how to set up: https://github.com/leonardoanalista/cz-customizable#steps "',
);
});
16 changes: 16 additions & 0 deletions __tests__/read-config.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
const readConfigFile = require('../lib/read-config-file');

const configInRootPackage = require('../.cz-config');
const configExampleInRootPackage = require('../cz-config-EXAMPLE');

// This is a unit test but it reads real a config file in te project root.
// It could be called "integration". The most important is it increase our confidence.
it('return config the nearest config in the root repo', () => {
const config = readConfigFile();
expect(config).toEqual(configInRootPackage);
});

it('return sample config when .cz-config.js does not exist in the repo root and user home directory', () => {
const config = readConfigFile('.configNonExists');
expect(config).toEqual(configExampleInRootPackage);
});
2 changes: 1 addition & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const temp = require('temp').track();
const fs = require('fs');
const log = require('./lib/logger');
const buildCommit = require('./lib/build-commit');
const readConfigFile = require('./lib/read-config');
const readConfigFile = require('./lib/read-config-file');

module.exports = {
prompter(cz, commit) {
Expand Down
8 changes: 4 additions & 4 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@ module.exports = {
// An object that configures minimum threshold enforcement for coverage results
coverageThreshold: {
global: {
branches: 90,
functions: 92,
lines: 88,
statements: 88,
branches: 96,
functions: 95,
lines: 96,
statements: 96,
},
},

Expand Down
18 changes: 9 additions & 9 deletions lib/build-commit.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,17 +48,17 @@ const addFooter = (footer, config) => {
return `\n\n${footerPrefix} ${addBreaklinesIfNeeded(footer, config.breaklineChar)}`;
};

const escapeSpecialChars = result => {
// eslint-disable-next-line no-useless-escape
const specialChars = ['`'];

const escapeSpecialChars = (result) => {
// eslint-disable-next-line no-useless-escape, prettier/prettier
const specialChars = ['`', '"', '\\$', '!', '<', '>', '&'];
let newResult = result;
// eslint-disable-next-line array-callback-return
specialChars.map(item => {
// If user types "feat: `string`", the commit preview should show "feat: `\string\`".
// Don't worry. The git log will be "feat: `string`"
newResult = result.replace(new RegExp(item, 'g'), '\\`');

specialChars.forEach((item) => {
// If user types `feat: "string"`, the commit preview should show `feat: \"string\"`.
// Don't worry. The git log will be `feat: "string"`
newResult = newResult.replace(new RegExp(item, 'g'), `\\${item}`);
});

return newResult;
};

Expand Down
10 changes: 5 additions & 5 deletions lib/read-config.js → lib/read-config-file.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
/* eslint-disable import/no-dynamic-require */
/* eslint-disable global-require */

const CZ_CONFIG_NAME = '.cz-config.js';
const findConfig = require('find-config');
const path = require('path');
const log = require('./logger');

// TODO: write unit tests
const readConfigFile = () => {
const readConfigFile = (CZ_CONFIG_NAME = '.cz-config.js') => {
// First try to find the .cz-config.js config file
// It seems like find-config still locates config files in the home directory despite of the home:false prop.
const czConfig = findConfig.require(CZ_CONFIG_NAME, { home: false });

if (czConfig) {
Expand All @@ -17,6 +15,7 @@ const readConfigFile = () => {

// fallback to locating it using the config block in the nearest package.json
let pkg = findConfig('package.json', { home: false });

if (pkg) {
const pkgDir = path.dirname(pkg);

Expand All @@ -32,7 +31,8 @@ const readConfigFile = () => {
}
}

log.warn(
/* istanbul ignore next */
log.error(
'Unable to find a configuration file. Please refer to documentation to learn how to set up: https://github.com/leonardoanalista/cz-customizable#steps "'
);
return null;
Expand Down
Loading

0 comments on commit bfe9214

Please sign in to comment.