@stats-organization/github-readme-stats-core 0.0.1

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 (140) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1 -0
  3. package/build/api/gist.d.ts +19 -0
  4. package/build/api/gist.d.ts.map +1 -0
  5. package/build/api/gist.js +77 -0
  6. package/build/api/index.d.ts +37 -0
  7. package/build/api/index.d.ts.map +1 -0
  8. package/build/api/index.js +115 -0
  9. package/build/api/pin.d.ts +27 -0
  10. package/build/api/pin.d.ts.map +1 -0
  11. package/build/api/pin.js +104 -0
  12. package/build/api/top-langs.d.ts +31 -0
  13. package/build/api/top-langs.d.ts.map +1 -0
  14. package/build/api/top-langs.js +121 -0
  15. package/build/api/wakatime.d.ts +28 -0
  16. package/build/api/wakatime.d.ts.map +1 -0
  17. package/build/api/wakatime.js +85 -0
  18. package/build/calculateRank.d.ts +28 -0
  19. package/build/calculateRank.d.ts.map +1 -0
  20. package/build/calculateRank.js +63 -0
  21. package/build/cards/gist.d.ts +21 -0
  22. package/build/cards/gist.d.ts.map +1 -0
  23. package/build/cards/gist.js +124 -0
  24. package/build/cards/repo.d.ts +21 -0
  25. package/build/cards/repo.d.ts.map +1 -0
  26. package/build/cards/repo.js +264 -0
  27. package/build/cards/stats.d.ts +49 -0
  28. package/build/cards/stats.d.ts.map +1 -0
  29. package/build/cards/stats.js +527 -0
  30. package/build/cards/top-languages.d.ts +143 -0
  31. package/build/cards/top-languages.d.ts.map +1 -0
  32. package/build/cards/top-languages.js +795 -0
  33. package/build/cards/wakatime.d.ts +16 -0
  34. package/build/cards/wakatime.d.ts.map +1 -0
  35. package/build/cards/wakatime.js +387 -0
  36. package/build/common/Card.d.ts +109 -0
  37. package/build/common/Card.d.ts.map +1 -0
  38. package/build/common/Card.js +241 -0
  39. package/build/common/I18n.d.ts +26 -0
  40. package/build/common/I18n.d.ts.map +1 -0
  41. package/build/common/I18n.js +34 -0
  42. package/build/common/color.d.ts +53 -0
  43. package/build/common/color.d.ts.map +1 -0
  44. package/build/common/color.js +92 -0
  45. package/build/common/config.d.ts +3 -0
  46. package/build/common/config.d.ts.map +1 -0
  47. package/build/common/config.js +69 -0
  48. package/build/common/constants.d.ts +6 -0
  49. package/build/common/constants.d.ts.map +1 -0
  50. package/build/common/constants.js +7 -0
  51. package/build/common/error.d.ts +53 -0
  52. package/build/common/error.d.ts.map +1 -0
  53. package/build/common/error.js +72 -0
  54. package/build/common/fmt.d.ts +27 -0
  55. package/build/common/fmt.d.ts.map +1 -0
  56. package/build/common/fmt.js +66 -0
  57. package/build/common/html.d.ts +10 -0
  58. package/build/common/html.d.ts.map +1 -0
  59. package/build/common/html.js +18 -0
  60. package/build/common/http.d.ts +9 -0
  61. package/build/common/http.d.ts.map +1 -0
  62. package/build/common/http.js +18 -0
  63. package/build/common/icons.d.ts +26 -0
  64. package/build/common/icons.d.ts.map +1 -0
  65. package/build/common/icons.js +77 -0
  66. package/build/common/languageColors.json +667 -0
  67. package/build/common/log.d.ts +9 -0
  68. package/build/common/log.d.ts.map +1 -0
  69. package/build/common/log.js +8 -0
  70. package/build/common/ops.d.ts +65 -0
  71. package/build/common/ops.d.ts.map +1 -0
  72. package/build/common/ops.js +137 -0
  73. package/build/common/render.d.ts +155 -0
  74. package/build/common/render.d.ts.map +1 -0
  75. package/build/common/render.js +361 -0
  76. package/build/common/retryer.d.ts +22 -0
  77. package/build/common/retryer.d.ts.map +1 -0
  78. package/build/common/retryer.js +86 -0
  79. package/build/fetchers/gist.d.ts +26 -0
  80. package/build/fetchers/gist.d.ts.map +1 -0
  81. package/build/fetchers/gist.js +99 -0
  82. package/build/fetchers/repo.d.ts +16 -0
  83. package/build/fetchers/repo.d.ts.map +1 -0
  84. package/build/fetchers/repo.js +116 -0
  85. package/build/fetchers/stats.d.ts +22 -0
  86. package/build/fetchers/stats.d.ts.map +1 -0
  87. package/build/fetchers/stats.js +339 -0
  88. package/build/fetchers/top-languages.d.ts +20 -0
  89. package/build/fetchers/top-languages.d.ts.map +1 -0
  90. package/build/fetchers/top-languages.js +139 -0
  91. package/build/fetchers/wakatime.d.ts +11 -0
  92. package/build/fetchers/wakatime.d.ts.map +1 -0
  93. package/build/fetchers/wakatime.js +25 -0
  94. package/build/index.d.ts +20 -0
  95. package/build/index.d.ts.map +1 -0
  96. package/build/index.js +19 -0
  97. package/build/themes/index.d.ts +469 -0
  98. package/build/themes/index.d.ts.map +1 -0
  99. package/build/themes/index.js +468 -0
  100. package/build/translations.d.ts +533 -0
  101. package/build/translations.d.ts.map +1 -0
  102. package/build/translations.js +1140 -0
  103. package/package.json +94 -0
  104. package/src/api/gist.js +99 -0
  105. package/src/api/index.js +185 -0
  106. package/src/api/pin.js +146 -0
  107. package/src/api/top-langs.js +168 -0
  108. package/src/api/wakatime.js +113 -0
  109. package/src/calculateRank.js +86 -0
  110. package/src/cards/gist.js +187 -0
  111. package/src/cards/repo.js +365 -0
  112. package/src/cards/stats.js +673 -0
  113. package/src/cards/top-languages.js +1023 -0
  114. package/src/cards/types.d.ts +78 -0
  115. package/src/cards/wakatime.js +471 -0
  116. package/src/common/Card.js +275 -0
  117. package/src/common/I18n.js +42 -0
  118. package/src/common/color.js +145 -0
  119. package/src/common/config.js +78 -0
  120. package/src/common/constants.js +9 -0
  121. package/src/common/error.js +90 -0
  122. package/src/common/fmt.js +80 -0
  123. package/src/common/html.js +22 -0
  124. package/src/common/http.js +21 -0
  125. package/src/common/icons.js +81 -0
  126. package/src/common/languageColors.json +667 -0
  127. package/src/common/log.ts +12 -0
  128. package/src/common/ops.js +172 -0
  129. package/src/common/render.js +442 -0
  130. package/src/common/retryer.js +104 -0
  131. package/src/fetchers/gist.js +114 -0
  132. package/src/fetchers/repo.js +165 -0
  133. package/src/fetchers/stats.js +478 -0
  134. package/src/fetchers/top-languages.js +177 -0
  135. package/src/fetchers/types.d.ts +128 -0
  136. package/src/fetchers/wakatime.js +37 -0
  137. package/src/index.ts +25 -0
  138. package/src/themes/README.md +229 -0
  139. package/src/themes/index.ts +476 -0
  140. package/src/translations.js +1156 -0
@@ -0,0 +1,365 @@
1
+ // @ts-check
2
+
3
+ import { Card } from "../common/Card.js";
4
+ import { I18n } from "../common/I18n.js";
5
+ import { getCardColors } from "../common/color.js";
6
+ import { kFormatter, wrapTextMultiline } from "../common/fmt.js";
7
+ import { encodeHTML } from "../common/html.js";
8
+ import { icons } from "../common/icons.js";
9
+ import { buildSearchFilter, clampValue, parseEmojis } from "../common/ops.js";
10
+ import {
11
+ countWrappedLines,
12
+ createLanguageNode,
13
+ flexLayout,
14
+ iconWithLabel,
15
+ measureText,
16
+ wrappedTextNode,
17
+ wrappedTextStyles,
18
+ } from "../common/render.js";
19
+ import { repoCardLocales } from "../translations.js";
20
+
21
+ import { createTextNode } from "./stats.js";
22
+
23
+ const ICON_SIZE = 16;
24
+ const CARD_DEFAULT_WIDTH = 400;
25
+ const X_OFFSET = 25;
26
+ const DESCRIPTION_FONT_SIZE = 13;
27
+ const DESCRIPTION_LINE_HEIGHT_PX = 16;
28
+ const DESCRIPTION_MAX_LINES = 3;
29
+
30
+ /**
31
+ * Retrieves the repository description and wraps it to fit the card width.
32
+ *
33
+ * @param {string} label The repository description.
34
+ * @param {string} textColor The color of the text.
35
+ * @returns {string} Wrapped repo description SVG object.
36
+ */
37
+ const getBadgeSVG = (label, textColor, xOffset = 0) => `
38
+ <g data-testid="badge" class="badge" transform="translate(${320 + xOffset}, -18)">
39
+ <rect stroke="${textColor}" stroke-width="1" width="70" height="20" x="-12" y="-14" ry="10" rx="10"></rect>
40
+ <text
41
+ x="23" y="-5"
42
+ alignment-baseline="central"
43
+ dominant-baseline="central"
44
+ text-anchor="middle"
45
+ fill="${textColor}"
46
+ >
47
+ ${label}
48
+ </text>
49
+ </g>
50
+ `;
51
+
52
+ /**
53
+ * @typedef {import("../fetchers/types").RepositoryData} RepositoryData Repository data.
54
+ * @typedef {import("./types").RepoCardOptions} RepoCardOptions Repo card options.
55
+ */
56
+
57
+ /**
58
+ * Renders repository card details.
59
+ *
60
+ * @param {RepositoryData} repo Repository data.
61
+ * @param {Partial<RepoCardOptions>} options Card options.
62
+ * @returns {string} Repository card SVG object.
63
+ */
64
+ const renderRepoCard = (repo, options = {}) => {
65
+ const {
66
+ name,
67
+ nameWithOwner,
68
+ description,
69
+ primaryLanguage,
70
+ isArchived,
71
+ isTemplate,
72
+ starCount,
73
+ forkCount,
74
+ totalPRsAuthored,
75
+ totalPRsCommented,
76
+ totalPRsReviewed,
77
+ totalIssuesAuthored,
78
+ totalIssuesCommented,
79
+ } = repo;
80
+ const {
81
+ hide_border = false,
82
+ title_color,
83
+ icon_color,
84
+ text_color,
85
+ bg_color,
86
+ card_width_input,
87
+ show_owner = false,
88
+ browser_rendering = false,
89
+ show = [],
90
+ show_icons = true,
91
+ number_format = "short",
92
+ text_bold = false,
93
+ line_height = 22,
94
+ username,
95
+ theme = "default_repocard",
96
+ border_radius,
97
+ border_color,
98
+ locale,
99
+ description_lines_count,
100
+ } = options;
101
+
102
+ const card_width =
103
+ card_width_input && !isNaN(card_width_input)
104
+ ? card_width_input
105
+ : show.length >= 2
106
+ ? CARD_DEFAULT_WIDTH + 30
107
+ : CARD_DEFAULT_WIDTH;
108
+
109
+ const i18n = new I18n({
110
+ locale,
111
+ translations: repoCardLocales,
112
+ });
113
+
114
+ let repoFilter = encodeURIComponent(buildSearchFilter([nameWithOwner], []));
115
+ const STATS = {};
116
+ if (show.includes("prs_authored")) {
117
+ STATS.prs_authored = {
118
+ icon: icons.prs,
119
+ label: i18n.t("repocard.prs-authored"),
120
+ value: totalPRsAuthored,
121
+ id: "prs_authored",
122
+ link: `https://github.com/search?q=${repoFilter}author%3A${username}&amp;type=pullrequests`,
123
+ };
124
+ }
125
+ if (show.includes("prs_commented")) {
126
+ STATS.prs_commented = {
127
+ icon: icons.comments,
128
+ label: i18n.t("repocard.prs-commented"),
129
+ value: totalPRsCommented,
130
+ id: "prs_commented",
131
+ link: `https://github.com/search?q=${repoFilter}commenter%3A${username}+-author%3A${username}&amp;type=pullrequests`,
132
+ };
133
+ }
134
+ if (show.includes("prs_reviewed")) {
135
+ STATS.prs_reviewed = {
136
+ icon: icons.reviews,
137
+ label: i18n.t("repocard.prs-reviewed"),
138
+ value: totalPRsReviewed,
139
+ id: "prs_reviewed",
140
+ link: `https://github.com/search?q=${repoFilter}reviewed-by%3A${username}+-author%3A${username}&amp;type=pullrequests`,
141
+ };
142
+ }
143
+ if (show.includes("issues_authored")) {
144
+ STATS.issues_authored = {
145
+ icon: icons.issues,
146
+ label: i18n.t("repocard.issues-authored"),
147
+ value: totalIssuesAuthored,
148
+ id: "issues_authored",
149
+ link: `https://github.com/search?q=${repoFilter}author%3A${username}&amp;type=issues`,
150
+ };
151
+ }
152
+ if (show.includes("issues_commented")) {
153
+ STATS.issues_commented = {
154
+ icon: icons.discussions_started,
155
+ label: i18n.t("repocard.issues-commented"),
156
+ value: totalIssuesCommented,
157
+ id: "issues_commented",
158
+ link: `https://github.com/search?q=${repoFilter}commenter%3A${username}+-author%3A${username}&amp;type=issues`,
159
+ };
160
+ }
161
+
162
+ const statItems = Object.keys(STATS).map((key, index) =>
163
+ // create the text nodes, and pass index so that we can calculate the line spacing
164
+ createTextNode({
165
+ icon: STATS[key].icon,
166
+ label: STATS[key].label,
167
+ value: STATS[key].value,
168
+ id: STATS[key].id,
169
+ unitSymbol: STATS[key].unitSymbol,
170
+ index,
171
+ showIcons: show_icons,
172
+ shiftValuePos: 14.01,
173
+ bold: text_bold,
174
+ numberFormat: number_format,
175
+ link: STATS[key].link,
176
+ labelXOffset: 23,
177
+ }),
178
+ );
179
+
180
+ const extraLHeight = parseInt(String(line_height), 10);
181
+ const lineHeight = 10;
182
+ const header = show_owner ? nameWithOwner : name;
183
+ const langName = (primaryLanguage && primaryLanguage.name) || "Unspecified";
184
+ const langColor = (primaryLanguage && primaryLanguage.color) || "#333";
185
+ const desc = parseEmojis(description || "No description provided");
186
+ const descriptionBoxWidth = card_width - 2 * X_OFFSET;
187
+
188
+ let descriptionLinesCount, descriptionSvg;
189
+ if (browser_rendering) {
190
+ // The browser performs the actual text wrapping inside the foreignObject;
191
+ // we only estimate the line count server-side so the SVG can reserve enough
192
+ // height. The estimate uses measureText for font-aware widths instead of a
193
+ // fixed character count.
194
+ descriptionLinesCount = description_lines_count
195
+ ? clampValue(description_lines_count, 1, DESCRIPTION_MAX_LINES)
196
+ : countWrappedLines(
197
+ desc,
198
+ DESCRIPTION_FONT_SIZE,
199
+ descriptionBoxWidth,
200
+ DESCRIPTION_MAX_LINES,
201
+ );
202
+ descriptionSvg = wrappedTextNode({
203
+ text: desc,
204
+ x: X_OFFSET,
205
+ y: -3,
206
+ width: descriptionBoxWidth,
207
+ height: descriptionLinesCount * DESCRIPTION_LINE_HEIGHT_PX + 10, // 10px extra for "descenders" like g, j, q, p, y
208
+ lineCount: descriptionLinesCount,
209
+ className: "description",
210
+ testId: "description-text",
211
+ });
212
+ } else {
213
+ const descriptionMaxLines = description_lines_count
214
+ ? clampValue(description_lines_count, 1, DESCRIPTION_MAX_LINES)
215
+ : DESCRIPTION_MAX_LINES;
216
+ const multiLineDescription = wrapTextMultiline(
217
+ desc,
218
+ descriptionBoxWidth,
219
+ DESCRIPTION_FONT_SIZE,
220
+ descriptionMaxLines,
221
+ );
222
+ descriptionLinesCount = description_lines_count
223
+ ? clampValue(description_lines_count, 1, DESCRIPTION_MAX_LINES)
224
+ : multiLineDescription.length;
225
+ descriptionSvg = multiLineDescription
226
+ .map(
227
+ (line) =>
228
+ `<tspan dy="1.2em" x="${X_OFFSET}">${encodeHTML(line)}</tspan>`,
229
+ )
230
+ .join("");
231
+ descriptionSvg = `<text class="description" x="${X_OFFSET}" y="-5">
232
+ ${descriptionSvg}
233
+ </text>`;
234
+ }
235
+
236
+ const extraHeight = Object.keys(STATS).length
237
+ ? -7 + (Math.ceil(statItems.length / 2) + 1) * extraLHeight
238
+ : 0;
239
+ const height =
240
+ (descriptionLinesCount > 1 ? 120 : 110) +
241
+ descriptionLinesCount * lineHeight +
242
+ extraHeight;
243
+
244
+ // returns theme based colors with proper overrides and defaults
245
+ const colors = getCardColors({
246
+ title_color,
247
+ icon_color,
248
+ text_color,
249
+ bg_color,
250
+ border_color,
251
+ theme,
252
+ });
253
+
254
+ const svgLanguage = primaryLanguage
255
+ ? createLanguageNode(langName, langColor)
256
+ : "";
257
+
258
+ const totalStars = kFormatter(starCount);
259
+ const totalForks = kFormatter(forkCount);
260
+ const svgStars = iconWithLabel(
261
+ icons.star,
262
+ totalStars,
263
+ "stargazers",
264
+ ICON_SIZE,
265
+ );
266
+ const svgForks = iconWithLabel(
267
+ icons.fork,
268
+ totalForks,
269
+ "forkcount",
270
+ ICON_SIZE,
271
+ );
272
+
273
+ const starAndForkCount = flexLayout({
274
+ items: [svgLanguage, svgStars, svgForks],
275
+ sizes: [
276
+ measureText(langName, 12),
277
+ ICON_SIZE + measureText(`${totalStars}`, 12),
278
+ ICON_SIZE + measureText(`${totalForks}`, 12),
279
+ ],
280
+ gap: 25,
281
+ }).join("");
282
+
283
+ let extraRows = [];
284
+ for (let i = 0; i < statItems.length; i += 2) {
285
+ extraRows.push(
286
+ flexLayout({
287
+ items: statItems.slice(i, i + 2),
288
+ gap: 210,
289
+ direction: "row",
290
+ }).join(""),
291
+ );
292
+ }
293
+ const extraItems = `
294
+ <svg x="0" y="0"><g transform="translate(-3, ${height - 52 - extraHeight})">
295
+ ${flexLayout({
296
+ items: extraRows,
297
+ gap: extraLHeight,
298
+ direction: "column",
299
+ }).join("")}
300
+ </g></svg>
301
+ `;
302
+
303
+ const card = new Card({
304
+ defaultTitle: header.length > 35 ? `${header.slice(0, 35)}...` : header,
305
+ titlePrefixIcon: icons.contribs,
306
+ width: card_width,
307
+ height,
308
+ border_radius,
309
+ colors,
310
+ });
311
+
312
+ card.disableAnimations();
313
+ card.setHideBorder(hide_border);
314
+ card.setHideTitle(false);
315
+ card.setCSS(`
316
+ .description {
317
+ font: 400 ${DESCRIPTION_FONT_SIZE}px 'Segoe UI', Ubuntu, Sans-Serif;fill: ${colors.textColor};
318
+ ${browser_rendering ? wrappedTextStyles(colors.textColor) : ""}
319
+ }
320
+ .gray { font: 400 12px 'Segoe UI', Ubuntu, Sans-Serif; fill: ${colors.textColor} }
321
+ .badge { font: 600 11px 'Segoe UI', Ubuntu, Sans-Serif; }
322
+ .badge rect { opacity: 0.2 }
323
+
324
+ .stat { font: 400 12px 'Segoe UI', Ubuntu, Sans-Serif; fill: ${colors.textColor} }
325
+ .stagger {
326
+ opacity: 0;
327
+ animation: fadeInAnimation 0.3s ease-in-out forwards;
328
+ }
329
+ .not_bold { font-weight: 400 }
330
+ .bold { font-weight: 700 }
331
+ .icon {
332
+ fill: ${colors.iconColor};
333
+ display: block;
334
+ }
335
+ `);
336
+
337
+ return card.render(`
338
+ ${
339
+ isTemplate
340
+ ? // @ts-ignore
341
+ getBadgeSVG(
342
+ i18n.t("repocard.template"),
343
+ colors.textColor,
344
+ card_width - CARD_DEFAULT_WIDTH,
345
+ )
346
+ : isArchived
347
+ ? // @ts-ignore
348
+ getBadgeSVG(
349
+ i18n.t("repocard.archived"),
350
+ colors.textColor,
351
+ card_width - CARD_DEFAULT_WIDTH,
352
+ )
353
+ : ""
354
+ }
355
+
356
+ ${descriptionSvg}
357
+
358
+ <g transform="translate(30, ${height - 75 - extraHeight})">
359
+ ${starAndForkCount}
360
+ </g>
361
+ ${extraItems}
362
+ `);
363
+ };
364
+
365
+ export { renderRepoCard };