forked from commitizen/cz-cli
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(configLoader): added fully compatible cosmiconfig support
Added fully compatible cosmiconfig support with BOM and encoding checks. Added json5 config format parser. Added tests. Some minor code style changes; Closes: commitizen#773
- Loading branch information
andrei
committed
Oct 31, 2023
1 parent
2e57fd0
commit 636874f
Showing
12 changed files
with
940 additions
and
226 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,4 +6,5 @@ artifacts/ | |
npm-debug.log | ||
.nyc_output | ||
test/tools/trigger-appveyor-tests.sh | ||
logo/*.png | ||
logo/*.png | ||
.idea |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
import { cosmiconfigSync, defaultLoadersSync } from 'cosmiconfig'; | ||
import JSON5 from 'json5'; | ||
import stripBom from 'strip-bom'; | ||
import isUTF8 from "is-utf8"; | ||
|
||
const moduleName = 'cz'; | ||
const fullModuleName = 'commitizen'; | ||
|
||
const searchPlaces = [ | ||
`.${moduleName}rc`, // .czrc | ||
`.${moduleName}rc.json`, | ||
`.${moduleName}rc.json5`, | ||
`.${moduleName}rc.yaml`, | ||
`.${moduleName}rc.yml`, | ||
`.${moduleName}rc.js`, | ||
`.${moduleName}rc.cjs`, | ||
`.${moduleName}rc.ts`, | ||
`.config/${moduleName}rc`, | ||
`.config/${moduleName}rc.json`, | ||
`.config/${moduleName}rc.json5`, | ||
`.config/${moduleName}rc.yaml`, | ||
`.config/${moduleName}rc.yml`, | ||
`.config/${moduleName}rc.js`, | ||
`.config/${moduleName}rc.ts`, | ||
`.config/${moduleName}rc.cjs`, | ||
`${moduleName}.config.js`, | ||
`${moduleName}.config.ts`, | ||
`${moduleName}.config.cjs`, | ||
`.${moduleName}.json`, // .cz.json | ||
`.${moduleName}.json5`, | ||
'package.json', | ||
]; | ||
|
||
function withSafeContentLoader(loader) { | ||
return function (filePath, content) { | ||
if (!isUTF8(Buffer.from(content, 'utf8'))) { | ||
throw new Error(`The config file at "${filePath}" contains invalid charset, expect utf8`); | ||
} | ||
return loader(filePath, stripBom(content)); | ||
} | ||
} | ||
|
||
function json5Loader(filePath, content) { | ||
try { | ||
return JSON5.parse(content) || null; | ||
} catch (err) { | ||
err.message = `Error parsing json at ${filePath}:\n${err.message}`; | ||
throw err; | ||
} | ||
} | ||
|
||
// no '.ts': withSafeContentLoader(defaultLoadersSync['.ts']), | ||
const loaders = { | ||
'.cjs': withSafeContentLoader(defaultLoadersSync['.js']), | ||
'.js': withSafeContentLoader(defaultLoadersSync['.js']), | ||
'.yml': withSafeContentLoader(defaultLoadersSync['.yaml']), | ||
'.json': withSafeContentLoader(json5Loader), | ||
'.json5': withSafeContentLoader(json5Loader), | ||
'.yaml': withSafeContentLoader(defaultLoadersSync['.yaml']), | ||
'.ts': withSafeContentLoader(defaultLoadersSync['.ts']), | ||
noExt: withSafeContentLoader(json5Loader) | ||
} | ||
|
||
const defaultConfigExplorer = cosmiconfigSync(moduleName, { | ||
packageProp: ['configs', fullModuleName], | ||
searchPlaces: searchPlaces, | ||
loaders: loaders, | ||
cache: false, | ||
}); | ||
|
||
/** | ||
* @deprecated | ||
*/ | ||
const deprecatedConfigExplorerFallback = cosmiconfigSync(moduleName, { | ||
packageProp: ['czConfig'], | ||
searchPlaces: ['package.json'], | ||
loaders: loaders, | ||
cache: false, | ||
}); | ||
|
||
export { searchPlaces, moduleName, defaultConfigExplorer, deprecatedConfigExplorerFallback }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import { defaultConfigExplorer, deprecatedConfigExplorerFallback } from "./cosmiconfigLoader"; | ||
import { isInTest } from "../common/util"; | ||
|
||
export default findConfigContent; | ||
/** | ||
* Find content of the configuration file | ||
* @param {String} cwd - partial path to configuration file | ||
* @return {Object|undefined} | ||
*/ | ||
function findConfigContent(cwd) { | ||
const maybeConfig = defaultConfigExplorer.search(cwd); | ||
if (maybeConfig) { | ||
return maybeConfig.config | ||
} else { | ||
const deprecatedConfig = findOldCzConfig(cwd); | ||
if (deprecatedConfig) { | ||
return deprecatedConfig | ||
} | ||
} | ||
|
||
return undefined; | ||
} | ||
|
||
/** | ||
* find old czConfig | ||
* | ||
* @deprecated | ||
* @param {string} [searchFrom] | ||
* @return {Object|undefined} | ||
*/ | ||
function findOldCzConfig(searchFrom) { | ||
const maybeDeprecatedConfig = deprecatedConfigExplorerFallback.search(searchFrom); | ||
if (maybeDeprecatedConfig) { | ||
showOldCzConfigDeprecationWarning(); | ||
return maybeDeprecatedConfig.config; | ||
} | ||
|
||
return undefined; | ||
} | ||
|
||
/** | ||
* @deprecated | ||
* @return void | ||
*/ | ||
function showOldCzConfigDeprecationWarning() { | ||
// Suppress during test | ||
if (!isInTest()) { | ||
console.error("\n********\nWARNING: This repository's package.json is using czConfig. czConfig will be deprecated in Commitizen 3. \nPlease use this instead:\n{\n \"config\": {\n \"commitizen\": {\n \"path\": \"./path/to/adapter\"\n }\n }\n}\nFor more information, see: http://commitizen.github.io/cz-cli/\n********\n"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,82 +1,60 @@ | ||
import fs from 'fs'; | ||
import path from 'path'; | ||
|
||
import stripJSONComments from 'strip-json-comments'; | ||
import isUTF8 from 'is-utf8'; | ||
import stripBom from 'strip-bom'; | ||
|
||
import { getNormalizedConfig } from '../configLoader'; | ||
import { defaultConfigExplorer, deprecatedConfigExplorerFallback } from "./cosmiconfigLoader"; | ||
import { isInTest } from "../common/util"; | ||
|
||
export default getConfigContent; | ||
|
||
/** | ||
* Read the content of a configuration file | ||
* - if not js or json: strip any comments | ||
* - if js or json: require it | ||
* @param {String} configPath - full path to configuration file | ||
* @return {Object} | ||
*/ | ||
function readConfigContent (configPath) { | ||
const parsedPath = path.parse(configPath) | ||
const isRcFile = parsedPath.ext !== '.js' && parsedPath.ext !== '.json'; | ||
const jsonString = readConfigFileContent(configPath); | ||
const parse = isRcFile ? | ||
(contents) => JSON.parse(stripJSONComments(contents)) : | ||
(contents) => JSON.parse(contents); | ||
|
||
try { | ||
const parsed = parse(jsonString); | ||
|
||
Object.defineProperty(parsed, 'configPath', { | ||
value: configPath | ||
}); | ||
|
||
return parsed; | ||
} catch (error) { | ||
error.message = [ | ||
`Parsing JSON at ${configPath} for commitizen config failed:`, | ||
error.mesasge | ||
].join('\n'); | ||
|
||
throw error; | ||
} | ||
} | ||
|
||
/** | ||
* Get content of the configuration file | ||
* @param {String} configPath - partial path to configuration file | ||
* @param {String} directory - directory path which will be joined with config argument | ||
* @return {Object} | ||
* @param {String} [configPath] - partial path to configuration file | ||
* @param {String} [baseDirectory] - directory path which will be joined with config argument | ||
* @return {Object|undefined} | ||
*/ | ||
function getConfigContent (configPath, baseDirectory) { | ||
if (!configPath) { | ||
return; | ||
} | ||
if (!configPath) { | ||
return; | ||
} | ||
|
||
const resolvedPath = path.resolve(baseDirectory, configPath); | ||
const configBasename = path.basename(resolvedPath); | ||
const resolvedPath = path.resolve(baseDirectory, configPath); | ||
|
||
if (!fs.existsSync(resolvedPath)) { | ||
return getNormalizedConfig(resolvedPath); | ||
const maybeConfig = defaultConfigExplorer.load(resolvedPath); | ||
if (maybeConfig) { | ||
return maybeConfig.config | ||
} else { | ||
const deprecatedConfig = loadOldCzConfig(resolvedPath); | ||
if (deprecatedConfig) { | ||
return deprecatedConfig | ||
} | ||
} | ||
|
||
const content = readConfigContent(resolvedPath); | ||
return getNormalizedConfig(configBasename, content); | ||
}; | ||
return undefined; | ||
} | ||
|
||
/** | ||
* Read proper content from config file. | ||
* If the chartset of the config file is not utf-8, one error will be thrown. | ||
* @param {String} configPath | ||
* @return {String} | ||
* load old czConfig from known place | ||
* | ||
* @deprecated | ||
* @param {string} [fullPath] | ||
* @return {Object|undefined} | ||
*/ | ||
function readConfigFileContent (configPath) { | ||
function loadOldCzConfig(fullPath) { | ||
const maybeDeprecatedConfig = deprecatedConfigExplorerFallback.load(fullPath); | ||
if (maybeDeprecatedConfig) { | ||
showOldCzConfigDeprecationWarning(); | ||
return maybeDeprecatedConfig.config; | ||
} | ||
|
||
let rawBufContent = fs.readFileSync(configPath); | ||
return undefined; | ||
} | ||
|
||
if (!isUTF8(rawBufContent)) { | ||
throw new Error(`The config file at "${configPath}" contains invalid charset, expect utf8`); | ||
/** | ||
* @deprecated | ||
* @return void | ||
*/ | ||
function showOldCzConfigDeprecationWarning() { | ||
// Suppress during test | ||
if (!isInTest()) { | ||
console.error("\n********\nWARNING: This repository's package.json is using czConfig. czConfig will be deprecated in Commitizen 3. \nPlease use this instead:\n{\n \"config\": {\n \"commitizen\": {\n \"path\": \"./path/to/adapter\"\n }\n }\n}\nFor more information, see: http://commitizen.github.io/cz-cli/\n********\n"); | ||
} | ||
|
||
return stripBom(rawBufContent.toString("utf8")); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.