Skip to content

Instantly share code, notes, and snippets.

@trevor-coleman
Last active September 7, 2021 00:14
Show Gist options
  • Save trevor-coleman/51f1730044e14081faaff098618aba36 to your computer and use it in GitHub Desktop.
Save trevor-coleman/51f1730044e14081faaff098618aba36 to your computer and use it in GitHub Desktop.
A script to add subdirectories as scopes to commitlint scope-enum.
const fs = require('fs');
/**
* scopePaths defines which paths will have their subdirectories added to the list of scopes.
*
* The top level path (src) is not included.
*
* So with the directory structure:
* .
* └── src
* ├── components
* │ ├── my-first-component
* │ │ └── first-component-subfolder
* │ └── my-second-compoent
* ├── services
* │ └── my-first-service
* └── hooks
* └── my-first-hook
*
* An object like this:
*
* {
* src: ['components', "hooks"]
* }
*
* The follwing scopes would be added:
*
* [
* 'components--my-first-component',
* 'components--my-second-component',
* 'hooks--my-first-hook
* ]
*/
const scopePaths = {
src: ['components', 'services', 'hooks'],
};
/**
* Top level scopes are sorted and appended to the list of scopes as-is.
*/
const topLevelScopes = [
'linting',
'husky',
'hygen',
'jest',
'cypress',
'dependencies',
'storybook',
'vscode',
'yarn',
];
/**
* @param {string} dir directory to scan
* @returns {string[]} an array of subdirectory names
*/
function getSubdirectories(dir) {
return fs
.readdirSync(dir, { withFileTypes: true })
.filter(dirent => dirent.isDirectory())
.map(dirent => dirent.name);
}
/**
* Scans a given folder for immediate subdirectories and returns a list of scope names.
* @param {string} topLevelDir the top level directory (`src`, `_templates`)
* @param {string} scopeDir the directory to be scanned for subdirectories
* @returns {string[]} an array of scopes in the format `scopeDir/subdirectory`
*/
function mapSubDirectoriesToScopeNames(topLevelDir, scopeDir) {
const scopeDirPath = `${topLevelDir}/${scopeDir}`;
const scopeSubDirs = getSubdirectories(scopeDirPath);
if (scopeSubDirs.length === 0) {
return;
}
const subScopes = scopeSubDirs.map(
scopeSubDir => `${scopeDir}--${scopeSubDir}`
);
return subScopes;
}
/**
* Generates a list of scopes for commitlint validation by scanning specified directories.
*
* - components--my-component
* - vscode
*
* @param {object} pathObj An object in the format `{dir: ['subdirA', 'subdirB', 'subdirC']}`
* @param {string[]} listOfScopes An array of strings to be added directly as scopes.
* @returns {string[]} An array of scopes e.g. `['components/my-component', 'eslint']`
*/
function getScopes(pathObj, listOfScopes) {
let scopes = [];
Object.keys(pathObj).forEach(topLevelDir => {
const scopeDirs = pathObj[topLevelDir];
scopeDirs.forEach(scopeDir => {
const scopesInScopeDir = mapSubDirectoriesToScopeNames(
topLevelDir,
scopeDir
);
scopes = [...scopes, ...scopesInScopeDir];
});
});
scopes = [...scopes.sort(), ...listOfScopes.sort()];
return scopes;
}
const logScopes = () => {
console.log(getScopes(scopePaths, topLevelScopes, ''));
};
const commitScopes = ctx => {
return getScopes(scopePaths, topLevelScopes);
};
module.exports = { commitScopes, logScopes };
const { commitScopes } = require('./commit-scopes');
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
//Include custom scopes by calling
'scope-enum': async ctx => [2, 'always', commitScopes(ctx)],
},
helpUrl: `
Commit messages must follow conventional commit format:
https://www.conventionalcommits.org/en/v1.0.0/#summary
type(optional-cope): subject
[optional body]
* To bypass pre-commit hooks run 'git commit --no-verify'
>>> Use "yarn commit" for interactive prompt. <<<
`,
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment