@stats-organization/github-readme-stats-core 2.1.5 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. package/build/api/gist.d.ts +2 -7
  2. package/build/api/gist.d.ts.map +1 -1
  3. package/build/api/gist.js +9 -41
  4. package/build/api/index.d.ts +3 -8
  5. package/build/api/index.d.ts.map +1 -1
  6. package/build/api/index.js +23 -45
  7. package/build/api/pin.d.ts +2 -7
  8. package/build/api/pin.d.ts.map +1 -1
  9. package/build/api/pin.js +9 -41
  10. package/build/api/top-langs.d.ts +2 -7
  11. package/build/api/top-langs.d.ts.map +1 -1
  12. package/build/api/top-langs.js +11 -55
  13. package/build/api/wakatime.d.ts +2 -7
  14. package/build/api/wakatime.d.ts.map +1 -1
  15. package/build/api/wakatime.js +9 -41
  16. package/build/cards/common-options.d.ts +13 -0
  17. package/build/cards/common-options.d.ts.map +1 -0
  18. package/build/cards/common-options.js +1 -0
  19. package/build/cards/gist.d.ts +11 -16
  20. package/build/cards/gist.d.ts.map +1 -1
  21. package/build/cards/gist.js +26 -31
  22. package/build/cards/repo.d.ts +19 -16
  23. package/build/cards/repo.d.ts.map +1 -1
  24. package/build/cards/repo.js +58 -60
  25. package/build/cards/stats.d.ts +29 -46
  26. package/build/cards/stats.d.ts.map +1 -1
  27. package/build/cards/stats.js +113 -181
  28. package/build/cards/top-languages.d.ts +81 -77
  29. package/build/cards/top-languages.d.ts.map +1 -1
  30. package/build/cards/top-languages.js +174 -200
  31. package/build/cards/wakatime.d.ts +21 -11
  32. package/build/cards/wakatime.d.ts.map +1 -1
  33. package/build/cards/wakatime.js +73 -111
  34. package/build/common/Card.d.ts +34 -13
  35. package/build/common/Card.d.ts.map +1 -1
  36. package/build/common/Card.js +73 -40
  37. package/build/common/I18n.d.ts +4 -2
  38. package/build/common/I18n.d.ts.map +1 -1
  39. package/build/common/color.d.ts +60 -18
  40. package/build/common/color.d.ts.map +1 -1
  41. package/build/common/color.js +102 -5
  42. package/build/common/config.d.ts +2 -1
  43. package/build/common/config.d.ts.map +1 -1
  44. package/build/common/config.js +16 -26
  45. package/build/common/constants.d.ts +2 -1
  46. package/build/common/constants.d.ts.map +1 -1
  47. package/build/common/date.d.ts +37 -0
  48. package/build/common/date.d.ts.map +1 -0
  49. package/build/common/date.js +31 -0
  50. package/build/common/http.d.ts +25 -2
  51. package/build/common/http.d.ts.map +1 -1
  52. package/build/common/http.js +26 -3
  53. package/build/common/icons.d.ts +1 -0
  54. package/build/common/icons.d.ts.map +1 -1
  55. package/build/common/icons.js +1 -0
  56. package/build/common/languageColors.d.ts +11 -0
  57. package/build/common/languageColors.d.ts.map +1 -0
  58. package/build/common/languageColors.js +15 -0
  59. package/build/common/languageColors.json +1 -0
  60. package/build/common/ops.d.ts +3 -10
  61. package/build/common/ops.d.ts.map +1 -1
  62. package/build/common/ops.js +3 -15
  63. package/build/common/render.d.ts +43 -6
  64. package/build/common/render.d.ts.map +1 -1
  65. package/build/common/render.js +77 -11
  66. package/build/common/retryer.d.ts +3 -2
  67. package/build/common/retryer.d.ts.map +1 -1
  68. package/build/common/retryer.js +1 -0
  69. package/build/fetchers/gist.d.ts.map +1 -1
  70. package/build/fetchers/gist.js +8 -39
  71. package/build/fetchers/repo.d.ts +12 -11
  72. package/build/fetchers/repo.d.ts.map +1 -1
  73. package/build/fetchers/repo.js +26 -67
  74. package/build/fetchers/stats.d.ts +24 -17
  75. package/build/fetchers/stats.d.ts.map +1 -1
  76. package/build/fetchers/stats.js +302 -183
  77. package/build/fetchers/top-languages.d.ts +10 -15
  78. package/build/fetchers/top-languages.d.ts.map +1 -1
  79. package/build/fetchers/top-languages.js +52 -103
  80. package/build/fetchers/types.d.ts +24 -20
  81. package/build/fetchers/types.d.ts.map +1 -1
  82. package/build/fetchers/wakatime.d.ts +9 -5
  83. package/build/fetchers/wakatime.d.ts.map +1 -1
  84. package/build/fetchers/wakatime.js +8 -4
  85. package/build/graphql/contributionsDocument.d.ts +18 -0
  86. package/build/graphql/contributionsDocument.d.ts.map +1 -0
  87. package/build/graphql/contributionsDocument.js +30 -0
  88. package/build/graphql/generated/common.d.ts +42 -0
  89. package/build/graphql/generated/common.d.ts.map +1 -0
  90. package/build/graphql/generated/common.js +2 -0
  91. package/build/graphql/generated/gist.d.ts +44 -0
  92. package/build/graphql/generated/gist.d.ts.map +1 -0
  93. package/build/graphql/generated/gist.js +27 -0
  94. package/build/graphql/generated/repo.d.ts +67 -0
  95. package/build/graphql/generated/repo.d.ts.map +1 -0
  96. package/build/graphql/generated/repo.js +30 -0
  97. package/build/graphql/generated/stats.d.ts +150 -0
  98. package/build/graphql/generated/stats.d.ts.map +1 -0
  99. package/build/graphql/generated/stats.js +95 -0
  100. package/build/graphql/generated/top-languages.d.ts +54 -0
  101. package/build/graphql/generated/top-languages.d.ts.map +1 -0
  102. package/build/graphql/generated/top-languages.js +27 -0
  103. package/build/graphql/graphqlDocument.d.ts +22 -0
  104. package/build/graphql/graphqlDocument.d.ts.map +1 -0
  105. package/build/graphql/graphqlDocument.js +8 -0
  106. package/build/graphql/reposContributedToDocument.d.ts +27 -0
  107. package/build/graphql/reposContributedToDocument.d.ts.map +1 -0
  108. package/build/graphql/reposContributedToDocument.js +59 -0
  109. package/build/index.d.ts +3 -1
  110. package/build/index.d.ts.map +1 -1
  111. package/build/index.js +2 -1
  112. package/build/themes/index.d.ts +32 -0
  113. package/build/themes/index.d.ts.map +1 -1
  114. package/build/themes/index.js +28 -0
  115. package/build/translations.d.ts +17 -0
  116. package/build/translations.d.ts.map +1 -1
  117. package/build/translations.js +17 -0
  118. package/package.json +13 -7
  119. package/src/_github-username-regex.d.ts +4 -0
  120. package/src/api/gist.js +10 -46
  121. package/src/api/index.js +30 -51
  122. package/src/api/pin.js +10 -46
  123. package/src/api/top-langs.js +13 -60
  124. package/src/api/wakatime.js +10 -46
  125. package/src/cards/common-options.ts +13 -0
  126. package/src/cards/{gist.js → gist.ts} +37 -37
  127. package/src/cards/{repo.js → repo.ts} +86 -68
  128. package/src/cards/{stats.js → stats.ts} +168 -219
  129. package/src/cards/{top-languages.js → top-languages.ts} +309 -265
  130. package/src/cards/{wakatime.js → wakatime.ts} +136 -130
  131. package/src/common/Card.ts +94 -49
  132. package/src/common/I18n.ts +6 -2
  133. package/src/common/color.ts +163 -14
  134. package/src/common/config.ts +22 -29
  135. package/src/common/constants.ts +3 -1
  136. package/src/common/date.ts +45 -0
  137. package/src/common/http.ts +43 -3
  138. package/src/common/icons.ts +1 -0
  139. package/src/common/languageColors.json +1 -0
  140. package/src/common/languageColors.ts +19 -0
  141. package/src/common/ops.ts +8 -18
  142. package/src/common/render.ts +114 -14
  143. package/src/common/retryer.ts +6 -5
  144. package/src/fetchers/gist.ts +15 -72
  145. package/src/fetchers/repo.ts +114 -0
  146. package/src/fetchers/stats.ts +743 -0
  147. package/src/fetchers/top-languages.ts +135 -0
  148. package/src/fetchers/types.ts +28 -20
  149. package/src/fetchers/{wakatime.js → wakatime.ts} +19 -5
  150. package/src/graphql/contributionsDocument.ts +44 -0
  151. package/src/graphql/generated/common.ts +26 -0
  152. package/src/graphql/generated/gist.ts +59 -0
  153. package/src/graphql/generated/repo.ts +90 -0
  154. package/src/graphql/generated/stats.ts +194 -0
  155. package/src/graphql/generated/top-languages.ts +75 -0
  156. package/src/graphql/graphqlDocument.ts +25 -0
  157. package/src/graphql/queries/gist.graphql +25 -0
  158. package/src/graphql/queries/repo.graphql +28 -0
  159. package/src/graphql/queries/stats.graphql +120 -0
  160. package/src/graphql/queries/top-languages.graphql +34 -0
  161. package/src/graphql/reposContributedToDocument.ts +86 -0
  162. package/src/index.ts +3 -1
  163. package/src/themes/index.ts +33 -0
  164. package/src/translations.ts +17 -0
  165. package/src/cards/types.d.ts +0 -78
  166. package/src/fetchers/repo.js +0 -161
  167. package/src/fetchers/stats.js +0 -473
  168. package/src/fetchers/top-languages.js +0 -175
  169. package/src/themes/README.md +0 -229
@@ -1,14 +1,15 @@
1
1
  import { Card } from "../common/Card.js";
2
2
  import { I18n } from "../common/I18n.js";
3
- import { getCardColors } from "../common/color.js";
3
+ import { getLightDarkColors } from "../common/color.js";
4
4
  import { CustomError } from "../common/error.js";
5
- import { kFormatter } from "../common/fmt.js";
6
- import { encodeHTML } from "../common/html.js";
7
5
  import { icons, rankIcon } from "../common/icons.js";
8
6
  import { buildSearchFilter, clampValue } from "../common/ops.js";
9
- import { flexLayout, measureText } from "../common/render.js";
7
+ import { createTextNode, flexLayout, measureText } from "../common/render.js";
8
+ import type { StatsData } from "../fetchers/types.js";
10
9
  import { statCardLocales, wakatimeCardLocales } from "../translations.js";
11
10
 
11
+ import type { CommonOptions } from "./common-options.js";
12
+
12
13
  const CARD_MIN_WIDTH = 287;
13
14
  const CARD_DEFAULT_WIDTH = 287;
14
15
  const RANK_CARD_MIN_WIDTH = 420;
@@ -16,12 +17,39 @@ const RANK_CARD_DEFAULT_WIDTH = 450;
16
17
  const RANK_ONLY_CARD_MIN_WIDTH = 290;
17
18
  const RANK_ONLY_CARD_DEFAULT_WIDTH = 290;
18
19
 
19
- /**
20
- * Long locales that need more space for text. Keep sorted alphabetically.
21
- *
22
- * @type {(keyof typeof wakatimeCardLocales["wakatimecard.title"])[]}
23
- */
24
- const LONG_LOCALES = [
20
+ type RankIcon = "default" | "github" | "percentile";
21
+
22
+ interface StatCardOptions extends CommonOptions {
23
+ hide: Array<string>;
24
+ show_icons: boolean;
25
+ hide_title: boolean;
26
+ card_width: number;
27
+ hide_rank: boolean;
28
+ include_all_commits: boolean;
29
+ commits_year: number;
30
+ line_height: number | string;
31
+ custom_title: string;
32
+ disable_animations: boolean;
33
+ number_format: string;
34
+ number_precision: number;
35
+ ring_color: string;
36
+ text_bold: boolean;
37
+ rank_icon: RankIcon;
38
+ show: Array<string>;
39
+ }
40
+
41
+ /** Meta data for a stat, used to build its text node and accessibility label. */
42
+ interface StatItem {
43
+ icon: string;
44
+ label: string;
45
+ value: number | string;
46
+ id: string;
47
+ unitSymbol?: string;
48
+ link?: string;
49
+ }
50
+
51
+ /** Long locales that need more space for text. Keep sorted alphabetically. */
52
+ const LONG_LOCALES: Array<string> = [
25
53
  "az",
26
54
  "bg",
27
55
  "cs",
@@ -50,99 +78,13 @@ const LONG_LOCALES = [
50
78
  "zh-tw",
51
79
  ];
52
80
 
53
- /**
54
- * Create a stats card text item.
55
- *
56
- * The caller must ensure that the passed `icon` and `link` are properly sanitized!
57
- *
58
- * @param {object} params Object that contains the createTextNode parameters.
59
- * @param {string} params.icon The sanitized icon to display.
60
- * @param {string} params.label The label to display.
61
- * @param {number} params.value The value to display.
62
- * @param {string} params.id The id of the stat.
63
- * @param {string=} params.unitSymbol The unit symbol of the stat.
64
- * @param {number} params.index The index of the stat.
65
- * @param {boolean} params.showIcons Whether to show icons.
66
- * @param {number} params.shiftValuePos Number of pixels the value has to be shifted to the right.
67
- * @param {boolean} params.bold Whether to bold the label.
68
- * @param {string} params.numberFormat The format of numbers on card.
69
- * @param {number=} params.numberPrecision The precision of numbers on card.
70
- * @param {string} params.link Sanitized url to link to.
71
- * @param {number} params.labelXOffset horizontal offset for label.
72
- * @returns {string} The stats card text item SVG object.
73
- */
74
- const createTextNode = ({
75
- icon,
76
- label,
77
- value,
78
- id,
79
- unitSymbol,
80
- index,
81
- showIcons,
82
- shiftValuePos,
83
- bold,
84
- numberFormat,
85
- numberPrecision,
86
- link,
87
- labelXOffset = 25,
88
- }) => {
89
- if (!Number.isFinite(labelXOffset)) {
90
- throw new Error(`Invalid labelXOffset: "${labelXOffset}"`);
91
- }
92
- if (!Number.isFinite(shiftValuePos)) {
93
- throw new Error(`Invalid shiftValuePos: "${shiftValuePos}"`);
94
- }
95
- if (!Number.isFinite(index)) {
96
- throw new Error(`Invalid index: "${index}"`);
97
- }
98
-
99
- const precision =
100
- typeof numberPrecision === "number" && !isNaN(numberPrecision)
101
- ? clampValue(numberPrecision, 0, 2)
102
- : undefined;
103
- const kValue =
104
- numberFormat.toLowerCase() === "long" || id === "prs_merged_percentage"
105
- ? value
106
- : kFormatter(value, precision);
107
- const staggerDelay = (index + 3) * 150;
108
-
109
- const labelOffset = showIcons ? `x="${labelXOffset}"` : "";
110
- const iconSvg = showIcons
111
- ? `
112
- <svg data-testid="icon" class="icon" viewBox="0 0 16 16" version="1.1" width="16" height="16">
113
- ${icon}
114
- </svg>
115
- `
116
- : "";
117
- return (
118
- `
119
- <g class="stagger" style="animation-delay: ${staggerDelay}ms" transform="translate(25, 0)">` +
120
- (link ? `<a href="${link}">` : "") +
121
- `
122
- ${iconSvg}
123
- <text class="stat ${
124
- bold ? " bold" : "not_bold"
125
- }" ${labelOffset} y="12.5">${encodeHTML(label)}:</text>
126
- <text
127
- class="stat ${bold ? " bold" : "not_bold"}"
128
- x="${(showIcons ? 140 : 120) + (bold ? 5 : 0) + shiftValuePos}"
129
- y="12.5"
130
- data-testid="${id}"
131
- >${kValue}${unitSymbol ? ` ${unitSymbol}` : ""}</text>` +
132
- (link ? "</a>" : "") +
133
- `
134
- </g>
135
- `
136
- );
137
- };
138
-
139
81
  /**
140
82
  * Calculates progress along the boundary of the circle, i.e. its circumference.
141
83
  *
142
- * @param {number} value The rank value to calculate progress for.
143
- * @returns {number} Progress value.
84
+ * @param value The rank value to calculate progress for.
85
+ * @returns Progress value.
144
86
  */
145
- const calculateCircleProgress = (value) => {
87
+ const calculateCircleProgress = (value: number): number => {
146
88
  const radius = 40;
147
89
  const c = Math.PI * (radius * 2);
148
90
 
@@ -160,10 +102,11 @@ const calculateCircleProgress = (value) => {
160
102
  * Retrieves the animation to display progress along the circumference of circle
161
103
  * from the beginning to the given value in a clockwise direction.
162
104
  *
163
- * @param {{progress: number}} progress The progress value to animate to.
164
- * @returns {string} Progress animation css.
105
+ * @param props The props object.
106
+ * @param props.progress The progress value to animate to.
107
+ * @returns Progress animation css.
165
108
  */
166
- const getProgressAnimation = ({ progress }) => {
109
+ const getProgressAnimation = ({ progress }: { progress: number }): string => {
167
110
  return `
168
111
  @keyframes rankAnimation {
169
112
  from {
@@ -179,24 +122,27 @@ const getProgressAnimation = ({ progress }) => {
179
122
  /**
180
123
  * Retrieves CSS styles for a card.
181
124
  *
182
- * @param {Object} colors The colors to use for the card.
183
- * @param {string} colors.titleColor The title color.
184
- * @param {string} colors.textColor The text color.
185
- * @param {string} colors.iconColor The icon color.
186
- * @param {string} colors.ringColor The ring color.
187
- * @param {boolean} colors.show_icons Whether to show icons.
188
- * @param {number} colors.progress The progress value to animate to.
189
- * @returns {string} Card CSS styles.
125
+ * @param colors The colors to use for the card.
126
+ * @param colors.textColor The text color.
127
+ * @param colors.iconColor The icon color.
128
+ * @param colors.ringColor The ring color.
129
+ * @param colors.show_icons Whether to show icons.
130
+ * @param colors.progress The progress value to animate to.
131
+ * @returns Card CSS styles.
190
132
  */
191
133
  const getStyles = ({
192
- // eslint-disable-next-line no-unused-vars
193
- titleColor,
194
134
  textColor,
195
135
  iconColor,
196
136
  ringColor,
197
137
  show_icons,
198
138
  progress,
199
- }) => {
139
+ }: {
140
+ textColor: string;
141
+ iconColor: string;
142
+ ringColor: string;
143
+ show_icons: boolean;
144
+ progress: number;
145
+ }): string => {
200
146
  return `
201
147
  .stat {
202
148
  font: 600 14px 'Segoe UI', Ubuntu, "Helvetica Neue", Sans-Serif; fill: ${textColor};
@@ -251,37 +197,39 @@ const getStyles = ({
251
197
  /**
252
198
  * Return the label for commits according to the selected options
253
199
  *
254
- * @param {boolean} include_all_commits Option to include all years
255
- * @param {number|undefined} commits_year Option to include only selected year
256
- * @param {I18n} i18n The I18n instance.
257
- * @returns {string} The label corresponding to the options.
200
+ * @param include_all_commits Option to include all years
201
+ * @param commits_year Option to include only selected year
202
+ * @param i18n The I18n instance.
203
+ * @returns The label corresponding to the options.
258
204
  */
259
- const getTotalCommitsYearLabel = (include_all_commits, commits_year, i18n) =>
205
+ const getTotalCommitsYearLabel = (
206
+ include_all_commits: boolean,
207
+ commits_year: number | undefined,
208
+ i18n: I18n,
209
+ ): string =>
260
210
  include_all_commits
261
211
  ? ""
262
212
  : commits_year
263
213
  ? ` (${commits_year})`
264
214
  : ` (${i18n.t("wakatimecard.lastyear")})`;
265
215
 
266
- /**
267
- * @typedef {import('../fetchers/types').StatsData} StatsData
268
- * @typedef {import('./types').StatCardOptions} StatCardOptions
269
- */
270
-
271
216
  /**
272
217
  * Renders the stats card.
273
218
  *
274
- * @param {StatsData} stats The stats data.
275
- * @param {Partial<StatCardOptions>} options The card options.
276
- * @returns {string} The stats card SVG object.
219
+ * @param stats The stats data.
220
+ * @param options The card options.
221
+ * @param username GitHub username, used to build stat search links.
222
+ * @param repo Repositories to scope the search links to.
223
+ * @param owner Owners to scope the search links to.
224
+ * @returns The stats card SVG object.
277
225
  */
278
226
  const renderStatsCard = (
279
- stats,
280
- options = {},
281
- username,
282
- repo = [],
283
- owner = [],
284
- ) => {
227
+ stats: StatsData,
228
+ options: Partial<StatCardOptions> = {},
229
+ username?: string,
230
+ repo: Array<string> = [],
231
+ owner: Array<string> = [],
232
+ ): string => {
285
233
  const {
286
234
  name,
287
235
  totalStars,
@@ -294,11 +242,13 @@ const renderStatsCard = (
294
242
  totalDiscussionsStarted,
295
243
  totalDiscussionsAnswered,
296
244
  contributedTo,
245
+ allTimeContributedTo,
297
246
  totalPRsAuthored,
298
247
  totalPRsCommented,
299
248
  totalPRsReviewed,
300
249
  totalIssuesAuthored,
301
250
  totalIssuesCommented,
251
+ totalContributions,
302
252
  rank,
303
253
  } = stats;
304
254
  const {
@@ -311,16 +261,9 @@ const renderStatsCard = (
311
261
  include_all_commits = false,
312
262
  commits_year,
313
263
  line_height = 25,
314
- title_color,
315
- ring_color,
316
- icon_color,
317
- text_color,
318
264
  text_bold = true,
319
- bg_color,
320
- theme = "default",
321
265
  custom_title,
322
266
  border_radius,
323
- border_color,
324
267
  number_format = "short",
325
268
  number_precision,
326
269
  locale,
@@ -331,17 +274,8 @@ const renderStatsCard = (
331
274
 
332
275
  const lheight = parseInt(String(line_height), 10);
333
276
 
334
- // returns theme based colors with proper overrides and defaults
335
- const { titleColor, iconColor, textColor, bgColor, borderColor, ringColor } =
336
- getCardColors({
337
- title_color,
338
- text_color,
339
- icon_color,
340
- bg_color,
341
- border_color,
342
- ring_color,
343
- theme,
344
- });
277
+ const { lightColors, darkColors } = getLightDarkColors(options);
278
+ const { iconColor, textColor, ringColor } = lightColors;
345
279
 
346
280
  const apostrophe = /s$/i.test(name.trim()) ? "" : "s";
347
281
  const i18n = new I18n({
@@ -353,15 +287,25 @@ const renderStatsCard = (
353
287
  });
354
288
 
355
289
  // Meta data for creating text nodes with createTextNode function
356
- const STATS = {};
290
+ const STATS: Record<string, StatItem> = {};
357
291
 
358
- STATS.stars = {
292
+ STATS["stars"] = {
359
293
  icon: icons.star,
360
294
  label: i18n.t("statcard.totalstars"),
361
295
  value: totalStars,
362
296
  id: "stars",
363
297
  };
364
- STATS.commits = {
298
+
299
+ if (show.includes("contributions")) {
300
+ STATS["contributions"] = {
301
+ icon: icons.contributions,
302
+ label: i18n.t("statcard.contributions"),
303
+ value: totalContributions,
304
+ id: "contributions",
305
+ };
306
+ }
307
+
308
+ STATS["commits"] = {
365
309
  icon: icons.commits,
366
310
  label: `${i18n.t("statcard.commits")}${getTotalCommitsYearLabel(
367
311
  include_all_commits,
@@ -371,7 +315,7 @@ const renderStatsCard = (
371
315
  value: totalCommits,
372
316
  id: "commits",
373
317
  };
374
- STATS.prs = {
318
+ STATS["prs"] = {
375
319
  icon: icons.prs,
376
320
  label: i18n.t("statcard.prs"),
377
321
  value: totalPRs,
@@ -379,7 +323,7 @@ const renderStatsCard = (
379
323
  };
380
324
 
381
325
  if (show.includes("prs_merged")) {
382
- STATS.prs_merged = {
326
+ STATS["prs_merged"] = {
383
327
  icon: icons.prs_merged,
384
328
  label: i18n.t("statcard.prs-merged"),
385
329
  value: totalPRsMerged,
@@ -388,11 +332,11 @@ const renderStatsCard = (
388
332
  }
389
333
 
390
334
  if (show.includes("prs_merged_percentage")) {
391
- STATS.prs_merged_percentage = {
335
+ STATS["prs_merged_percentage"] = {
392
336
  icon: icons.prs_merged_percentage,
393
337
  label: i18n.t("statcard.prs-merged-percentage"),
394
338
  value: mergedPRsPercentage.toFixed(
395
- typeof number_precision === "number" && !isNaN(number_precision)
339
+ number_precision !== undefined && Number.isFinite(number_precision)
396
340
  ? clampValue(number_precision, 0, 2)
397
341
  : 2,
398
342
  ),
@@ -402,7 +346,7 @@ const renderStatsCard = (
402
346
  }
403
347
 
404
348
  if (show.includes("reviews")) {
405
- STATS.reviews = {
349
+ STATS["reviews"] = {
406
350
  icon: icons.reviews,
407
351
  label: i18n.t("statcard.reviews"),
408
352
  value: totalReviews,
@@ -410,7 +354,7 @@ const renderStatsCard = (
410
354
  };
411
355
  }
412
356
 
413
- STATS.issues = {
357
+ STATS["issues"] = {
414
358
  icon: icons.issues,
415
359
  label: i18n.t("statcard.issues"),
416
360
  value: totalIssues,
@@ -418,7 +362,7 @@ const renderStatsCard = (
418
362
  };
419
363
 
420
364
  if (show.includes("discussions_started")) {
421
- STATS.discussions_started = {
365
+ STATS["discussions_started"] = {
422
366
  icon: icons.discussions_started,
423
367
  label: i18n.t("statcard.discussions-started"),
424
368
  value: totalDiscussionsStarted,
@@ -426,7 +370,7 @@ const renderStatsCard = (
426
370
  };
427
371
  }
428
372
  if (show.includes("discussions_answered")) {
429
- STATS.discussions_answered = {
373
+ STATS["discussions_answered"] = {
430
374
  icon: icons.discussions_answered,
431
375
  label: i18n.t("statcard.discussions-answered"),
432
376
  value: totalDiscussionsAnswered,
@@ -434,10 +378,10 @@ const renderStatsCard = (
434
378
  };
435
379
  }
436
380
 
437
- let repoFilter = encodeURIComponent(buildSearchFilter(repo, owner));
438
- const encodedUsername = encodeURIComponent(username);
381
+ const repoFilter = encodeURIComponent(buildSearchFilter(repo, owner));
382
+ const encodedUsername = encodeURIComponent(username ?? "");
439
383
  if (show.includes("prs_authored")) {
440
- STATS.prs_authored = {
384
+ STATS["prs_authored"] = {
441
385
  icon: icons.prs,
442
386
  label: i18n.t("statcard.prs-authored"),
443
387
  value: totalPRsAuthored,
@@ -446,7 +390,7 @@ const renderStatsCard = (
446
390
  };
447
391
  }
448
392
  if (show.includes("prs_commented")) {
449
- STATS.prs_commented = {
393
+ STATS["prs_commented"] = {
450
394
  icon: icons.comments,
451
395
  label: i18n.t("statcard.prs-commented"),
452
396
  value: totalPRsCommented,
@@ -455,7 +399,7 @@ const renderStatsCard = (
455
399
  };
456
400
  }
457
401
  if (show.includes("prs_reviewed")) {
458
- STATS.prs_reviewed = {
402
+ STATS["prs_reviewed"] = {
459
403
  icon: icons.reviews,
460
404
  label: i18n.t("statcard.prs-reviewed"),
461
405
  value: totalPRsReviewed,
@@ -464,7 +408,7 @@ const renderStatsCard = (
464
408
  };
465
409
  }
466
410
  if (show.includes("issues_authored")) {
467
- STATS.issues_authored = {
411
+ STATS["issues_authored"] = {
468
412
  icon: icons.issues,
469
413
  label: i18n.t("statcard.issues-authored"),
470
414
  value: totalIssuesAuthored,
@@ -473,7 +417,7 @@ const renderStatsCard = (
473
417
  };
474
418
  }
475
419
  if (show.includes("issues_commented")) {
476
- STATS.issues_commented = {
420
+ STATS["issues_commented"] = {
477
421
  icon: icons.discussions_started,
478
422
  label: i18n.t("statcard.issues-commented"),
479
423
  value: totalIssuesCommented,
@@ -482,45 +426,49 @@ const renderStatsCard = (
482
426
  };
483
427
  }
484
428
 
485
- STATS.contribs = {
429
+ STATS["contribs"] = {
486
430
  icon: icons.contribs,
487
431
  label: i18n.t("statcard.contribs"),
488
432
  value: contributedTo,
489
433
  id: "contribs",
490
434
  };
491
435
 
492
- // @ts-ignore
436
+ if (show.includes("all_time_contribs")) {
437
+ STATS["all_time_contribs"] = {
438
+ icon: icons.contribs,
439
+ label: i18n.t("statcard.all-time-contribs"),
440
+ value: allTimeContributedTo,
441
+ id: "all_time_contribs",
442
+ };
443
+ }
444
+
493
445
  const isLongLocale = locale ? LONG_LOCALES.includes(locale) : false;
494
446
 
495
447
  // check if all used labels are short
496
- const longLabels =
497
- Object.keys(STATS)
498
- .filter((key) => !hide.includes(key))
499
- .filter((key) => STATS[key].label.length > 18).length > 0;
448
+ const longLabels = Object.entries(STATS)
449
+ .filter(([key]) => !hide.includes(key))
450
+ .some(([, stat]) => stat.label.length > 18);
500
451
 
501
452
  // filter out hidden stats defined by user & create the text nodes
502
- const statItems = Object.keys(STATS)
503
- .filter((key) => !hide.includes(key))
504
- .map((key, index) => {
505
- // @ts-ignore
506
- const stats = STATS[key];
507
-
508
- // create the text nodes, and pass index so that we can calculate the line spacing
509
- return createTextNode({
510
- icon: stats.icon,
511
- label: stats.label,
512
- value: stats.value,
513
- id: stats.id,
514
- unitSymbol: stats.unitSymbol,
453
+ const statItems = Object.entries(STATS)
454
+ .filter(([key]) => !hide.includes(key))
455
+ // pass index so that we can calculate the line spacing
456
+ .map(([, stat], index) =>
457
+ createTextNode({
458
+ icon: stat.icon,
459
+ label: stat.label,
460
+ value: stat.value,
461
+ id: stat.id,
462
+ unitSymbol: stat.unitSymbol,
515
463
  index,
516
464
  showIcons: show_icons,
517
465
  shiftValuePos: 29.01 + (longLabels ? 50 : 0) + (isLongLocale ? 50 : 0),
518
466
  bold: text_bold,
519
467
  numberFormat: number_format,
520
468
  numberPrecision: number_precision,
521
- link: STATS[key].link,
522
- });
523
- });
469
+ link: stat.link,
470
+ }),
471
+ );
524
472
 
525
473
  if (statItems.length === 0 && hide_rank) {
526
474
  throw new CustomError(
@@ -531,21 +479,13 @@ const renderStatsCard = (
531
479
 
532
480
  // Calculate the card height depending on how many items there are
533
481
  // but if rank circle is visible clamp the minimum height to `150`
534
- let height = Math.max(
482
+ const height = Math.max(
535
483
  45 + (statItems.length + 1) * lheight,
536
484
  hide_rank ? 0 : statItems.length ? 150 : 180,
537
485
  );
538
486
 
539
487
  // the lower the user's percentile the better
540
488
  const progress = 100 - rank.percentile;
541
- const cssStyles = getStyles({
542
- titleColor,
543
- ringColor,
544
- textColor,
545
- iconColor,
546
- show_icons,
547
- progress,
548
- });
549
489
 
550
490
  const calculateTextWidth = () => {
551
491
  return measureText(
@@ -579,7 +519,7 @@ const renderStatsCard = (
579
519
  : statItems.length
580
520
  ? RANK_CARD_DEFAULT_WIDTH
581
521
  : RANK_ONLY_CARD_DEFAULT_WIDTH) + iconWidth;
582
- let width = card_width
522
+ const width = card_width
583
523
  ? isNaN(card_width)
584
524
  ? Math.max(defaultCardWidth, minCardWidth)
585
525
  : card_width
@@ -593,18 +533,29 @@ const renderStatsCard = (
593
533
  width,
594
534
  height,
595
535
  border_radius,
596
- colors: {
597
- titleColor,
598
- textColor,
599
- iconColor,
600
- bgColor,
601
- borderColor,
602
- },
536
+ colors: { light: lightColors, dark: darkColors },
603
537
  });
604
538
 
605
539
  card.setHideBorder(hide_border);
606
540
  card.setHideTitle(hide_title);
607
- card.setCSS(cssStyles);
541
+ card.setCSS({
542
+ light: getStyles({
543
+ ringColor,
544
+ textColor,
545
+ iconColor,
546
+ show_icons,
547
+ progress,
548
+ }),
549
+ dark: darkColors
550
+ ? getStyles({
551
+ ringColor: darkColors.ringColor,
552
+ textColor: darkColors.textColor,
553
+ iconColor: darkColors.iconColor,
554
+ show_icons,
555
+ progress,
556
+ })
557
+ : null,
558
+ });
608
559
 
609
560
  if (disable_animations) {
610
561
  card.disableAnimations();
@@ -618,9 +569,9 @@ const renderStatsCard = (
618
569
  * width < RANK_CARD_DEFAULT_WIDTH: The left and right padding will be enlarged
619
570
  * equally from a certain minimum at RANK_CARD_MIN_WIDTH.
620
571
  *
621
- * @returns {number} - Rank circle translation value.
572
+ * @returns Rank circle translation value.
622
573
  */
623
- const calculateRankXTranslation = () => {
574
+ const calculateRankXTranslation = (): number => {
624
575
  if (statItems.length) {
625
576
  const minXTranslation = RANK_CARD_MIN_WIDTH + iconWidth - 70;
626
577
  if (width > RANK_CARD_DEFAULT_WIDTH) {
@@ -644,24 +595,22 @@ const renderStatsCard = (
644
595
  <circle class="rank-circle-rim" cx="-10" cy="8" r="40" />
645
596
  <circle class="rank-circle" cx="-10" cy="8" r="40" />
646
597
  <g class="rank-text">
647
- ${rankIcon(rank_icon, rank?.level, rank?.percentile)}
598
+ ${rankIcon(rank_icon, rank.level, rank.percentile)}
648
599
  </g>
649
600
  </g>`;
650
601
 
651
602
  // Accessibility Labels
652
- const labels = Object.keys(STATS)
653
- .filter((key) => !hide.includes(key))
654
- .map((key) => {
655
- // @ts-ignore
656
- const stats = STATS[key];
603
+ const labels = Object.entries(STATS)
604
+ .filter(([key]) => !hide.includes(key))
605
+ .map(([key, stat]) => {
657
606
  if (key === "commits") {
658
607
  return `${i18n.t("statcard.commits")} ${getTotalCommitsYearLabel(
659
608
  include_all_commits,
660
609
  commits_year,
661
610
  i18n,
662
- )} : ${stats.value}`;
611
+ )} : ${stat.value}`;
663
612
  }
664
- return `${stats.label}: ${stats.value}`;
613
+ return `${stat.label}: ${stat.value}`;
665
614
  })
666
615
  .join(", ");
667
616
 
@@ -682,4 +631,4 @@ const renderStatsCard = (
682
631
  `);
683
632
  };
684
633
 
685
- export { renderStatsCard, createTextNode };
634
+ export { renderStatsCard };