get-changelog-config.js 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. "use strict";
  2. const log = require("npmlog");
  3. const npa = require("npm-package-arg");
  4. const pify = require("pify");
  5. const { ValidationError } = require("@lerna/validation-error");
  6. module.exports.getChangelogConfig = getChangelogConfig;
  7. const cfgCache = new Map();
  8. function isFunction(config) {
  9. return Object.prototype.toString.call(config) === "[object Function]";
  10. }
  11. function resolveConfigPromise(presetPackageName, presetConfig) {
  12. log.verbose("getChangelogConfig", "Attempting to resolve preset %j", presetPackageName);
  13. // eslint-disable-next-line global-require, import/no-dynamic-require
  14. let config = require(presetPackageName);
  15. log.info("getChangelogConfig", "Successfully resolved preset %j", presetPackageName);
  16. if (isFunction(config)) {
  17. try {
  18. // try assuming config builder function first
  19. config = config(presetConfig);
  20. } catch (_) {
  21. // legacy presets export an errback function instead of Q.all()
  22. config = pify(config)();
  23. }
  24. }
  25. return config;
  26. }
  27. /**
  28. * @param {import("..").ChangelogPresetConfig} [changelogPreset]
  29. * @param {string} [rootPath]
  30. */
  31. function getChangelogConfig(changelogPreset = "conventional-changelog-angular", rootPath) {
  32. const presetName = typeof changelogPreset === "string" ? changelogPreset : changelogPreset.name;
  33. const presetConfig = typeof changelogPreset === "object" ? changelogPreset : {};
  34. const cacheKey = `${presetName}${presetConfig ? JSON.stringify(presetConfig) : ""}`;
  35. let config = cfgCache.get(cacheKey);
  36. if (!config) {
  37. let presetPackageName = presetName;
  38. // https://github.com/npm/npm-package-arg#result-object
  39. const parsed = npa(presetPackageName, rootPath);
  40. log.verbose("getChangelogConfig", "using preset %j", presetPackageName);
  41. log.silly("npa", parsed);
  42. if (parsed.type === "directory") {
  43. if (parsed.raw[0] === "@") {
  44. // npa parses scoped subpath reference as a directory
  45. parsed.name = parsed.raw;
  46. parsed.scope = parsed.raw.substring(0, parsed.raw.indexOf("/"));
  47. // un-scoped subpath shorthand handled in first catch block
  48. } else {
  49. presetPackageName = parsed.fetchSpec;
  50. }
  51. } else if (parsed.type === "git" && parsed.hosted && parsed.hosted.default === "shortcut") {
  52. // probably a shorthand subpath, e.g. "foo/bar"
  53. parsed.name = parsed.raw;
  54. }
  55. // Maybe it doesn't need an implicit 'conventional-changelog-' prefix?
  56. try {
  57. config = resolveConfigPromise(presetPackageName, presetConfig);
  58. cfgCache.set(cacheKey, config);
  59. // early exit, yay
  60. return Promise.resolve(config);
  61. } catch (err) {
  62. log.verbose("getChangelogConfig", err.message);
  63. log.info("getChangelogConfig", "Auto-prefixing conventional-changelog preset %j", presetName);
  64. // probably a deep shorthand subpath :P
  65. parsed.name = parsed.raw;
  66. }
  67. if (parsed.name.indexOf("conventional-changelog-") < 0) {
  68. // implicit 'conventional-changelog-' prefix
  69. const parts = parsed.name.split("/");
  70. const start = parsed.scope ? 1 : 0;
  71. // foo => conventional-changelog-foo
  72. // @scope/foo => @scope/conventional-changelog-foo
  73. parts.splice(start, 1, `conventional-changelog-${parts[start]}`);
  74. // _technically_ supports 'foo/lib/bar.js', but that's gross
  75. presetPackageName = parts.join("/");
  76. }
  77. try {
  78. config = resolveConfigPromise(presetPackageName, presetConfig);
  79. cfgCache.set(cacheKey, config);
  80. } catch (err) {
  81. log.warn("getChangelogConfig", err.message);
  82. throw new ValidationError(
  83. "EPRESET",
  84. `Unable to load conventional-changelog preset '${presetName}'${
  85. presetName !== presetPackageName ? ` (${presetPackageName})` : ""
  86. }`
  87. );
  88. }
  89. }
  90. // the core presets are bloody Q.all() spreads
  91. return Promise.resolve(config);
  92. }