ghost-github-portfolio 0.3.5 → 0.4.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.
- package/README.md +25 -184
- package/dist/config.d.ts +2 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +91 -63
- package/dist/config.js.map +1 -1
- package/dist/generator.d.ts +36 -2
- package/dist/generator.d.ts.map +1 -1
- package/dist/generator.js +181 -180
- package/dist/generator.js.map +1 -1
- package/dist/ghost.d.ts +8 -0
- package/dist/ghost.d.ts.map +1 -1
- package/dist/ghost.js +35 -19
- package/dist/ghost.js.map +1 -1
- package/dist/github.d.ts +26 -7
- package/dist/github.d.ts.map +1 -1
- package/dist/github.js +91 -63
- package/dist/github.js.map +1 -1
- package/dist/http.d.ts +9 -0
- package/dist/http.d.ts.map +1 -1
- package/dist/http.js +47 -1
- package/dist/http.js.map +1 -1
- package/dist/index.js +55 -56
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +15 -25
- package/dist/types.d.ts.map +1 -1
- package/package.json +6 -6
package/dist/generator.js
CHANGED
|
@@ -1,195 +1,146 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
export const MAX_DESCRIPTION_LENGTH = 140;
|
|
2
|
+
// Abbreviations that end in a period without ending the sentence.
|
|
3
|
+
const ABBREVIATION = /\b(?:e\.g|i\.e|etc|vs|approx|incl|inc|ltd|dr|mr|ms|st)$/i;
|
|
4
|
+
/**
|
|
5
|
+
* Turn a GitHub description into one short line: emoji and :shortcodes:
|
|
6
|
+
* removed, em and en dashes turned into commas, first sentence only,
|
|
7
|
+
* at most 140 characters.
|
|
8
|
+
*/
|
|
9
|
+
export function cleanDescription(text, max = MAX_DESCRIPTION_LENGTH) {
|
|
10
|
+
let s = text
|
|
11
|
+
.replace(/:(?=[a-z0-9_+-]*[a-z])[a-z0-9_+-]+:/g, " ")
|
|
12
|
+
.replace(/(?![©®™])\p{Extended_Pictographic}/gu, " ")
|
|
13
|
+
.replace(/[\u{1F1E6}-\u{1F1FF}\u{1F3FB}-\u{1F3FF}️⃣]/gu, "")
|
|
14
|
+
.replace(/\s*[\u2013\u2014]\s*/g, ", ")
|
|
15
|
+
.replace(/\s+/g, " ")
|
|
16
|
+
.replace(/\s+([,.;:!?])/g, "$1")
|
|
17
|
+
.replace(/,(?=[,.;:!?])/g, "")
|
|
18
|
+
.replace(/^[\s,;:.-]+/, "")
|
|
19
|
+
.replace(/[\s,;:-]+$/, "")
|
|
20
|
+
.trim();
|
|
21
|
+
for (const m of s.matchAll(/[.!?](?=\s+["'(\p{Lu}\d])/gu)) {
|
|
22
|
+
const before = s.slice(0, m.index);
|
|
23
|
+
if (m[0] === "." && ABBREVIATION.test(before))
|
|
24
|
+
continue;
|
|
25
|
+
s = s.slice(0, m.index + 1);
|
|
26
|
+
break;
|
|
27
|
+
}
|
|
28
|
+
if (s.length > max) {
|
|
29
|
+
const cut = s.slice(0, max - 1);
|
|
30
|
+
const space = cut.lastIndexOf(" ");
|
|
31
|
+
s = (space > max / 2 ? cut.slice(0, space) : cut).replace(/[\s,;:.-]+$/, "") + "…";
|
|
32
|
+
}
|
|
33
|
+
else if (s && !/[.!?…]$/.test(s)) {
|
|
34
|
+
s += ".";
|
|
35
|
+
}
|
|
36
|
+
return s;
|
|
7
37
|
}
|
|
8
|
-
function
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
// Docker pulls (from config override)
|
|
21
|
-
const dockerImage = overrides?.dockerImage;
|
|
22
|
-
if (dockerImage) {
|
|
23
|
-
badges.push(`<a href="https://hub.docker.com/r/${dockerImage}"><img src="https://img.shields.io/docker/pulls/${dockerImage}?style=${style}" alt="Docker Pulls"></a>`);
|
|
24
|
-
}
|
|
25
|
-
// Website badge (from GitHub homepage or config)
|
|
26
|
-
const homepage = repo.homepage;
|
|
27
|
-
if (homepage &&
|
|
28
|
-
!homepage.includes("hub.docker.com") &&
|
|
29
|
-
!homepage.includes("github.io")) {
|
|
30
|
-
const domain = new URL(homepage).hostname;
|
|
31
|
-
badges.push(`<a href="${homepage}"><img src="${shieldsUrl("website", domain, "blue", style)}" alt="Website"></a>`);
|
|
32
|
-
}
|
|
33
|
-
// Custom badges from config
|
|
34
|
-
if (overrides?.badges) {
|
|
35
|
-
for (const badge of overrides.badges) {
|
|
36
|
-
badges.push(renderCustomBadge(badge, style));
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
// Auto-detect awesome-list from topics
|
|
40
|
-
if (repo.topics.includes("awesome-list") &&
|
|
41
|
-
!overrides?.badges?.some((b) => b.type === "awesome-list")) {
|
|
42
|
-
badges.push(`<img src="${shieldsUrl("awesome", "list", "fc60a8", style, "awesomelists")}" alt="Awesome List">`);
|
|
38
|
+
export function formatStars(count) {
|
|
39
|
+
return `${count.toLocaleString("en-US")} ${count === 1 ? "star" : "stars"}`;
|
|
40
|
+
}
|
|
41
|
+
/** The card's site link: the override, else the GitHub homepage unless it points at github.com. */
|
|
42
|
+
export function siteLink(repo, config) {
|
|
43
|
+
const override = config.portfolio.repos[repo.name];
|
|
44
|
+
const raw = (override?.homepage ?? repo.homepage ?? "").trim();
|
|
45
|
+
if (!raw)
|
|
46
|
+
return null;
|
|
47
|
+
let url;
|
|
48
|
+
try {
|
|
49
|
+
url = new URL(raw);
|
|
43
50
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
!overrides?.badges?.some((b) => b.type === "docs")) {
|
|
47
|
-
badges.push(`<a href="${homepage}"><img src="${shieldsUrl("docs", "online", "blue", style)}" alt="Docs"></a>`);
|
|
51
|
+
catch {
|
|
52
|
+
return null;
|
|
48
53
|
}
|
|
49
|
-
|
|
54
|
+
if (url.protocol !== "https:" && url.protocol !== "http:")
|
|
55
|
+
return null;
|
|
56
|
+
const host = url.hostname.toLowerCase();
|
|
57
|
+
if (host === "github.com" || host === "www.github.com")
|
|
58
|
+
return null;
|
|
59
|
+
return { url: raw, label: override?.siteLabel || "Site" };
|
|
50
60
|
}
|
|
51
|
-
function
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
return `<a href="https://hub.docker.com/r/${badge.label}"><img src="https://img.shields.io/docker/pulls/${badge.label}?style=${style}" alt="Docker Pulls"></a>`;
|
|
57
|
-
case "awesome-list":
|
|
58
|
-
return `<img src="${shieldsUrl("awesome", "list", badge.color ?? "fc60a8", style, "awesomelists")}" alt="Awesome List">`;
|
|
59
|
-
case "platform":
|
|
60
|
-
return `<img src="${shieldsUrl("platform", badge.label ?? "unknown", badge.color ?? "000", style, badge.logo)}" alt="Platform">`;
|
|
61
|
-
case "docs":
|
|
62
|
-
return `<a href="${badge.url}"><img src="${shieldsUrl("docs", badge.label ?? "online", badge.color ?? "blue", style)}" alt="Docs"></a>`;
|
|
63
|
-
case "custom":
|
|
64
|
-
return `<img src="${shieldsUrl(badge.label ?? "", badge.value ?? "", badge.color ?? "grey", style, badge.logo)}" alt="${badge.label}">`;
|
|
65
|
-
}
|
|
61
|
+
export function repoDescription(repo, config) {
|
|
62
|
+
const override = config.portfolio.repos[repo.name]?.description;
|
|
63
|
+
if (override)
|
|
64
|
+
return override;
|
|
65
|
+
return repo.description ? cleanDescription(repo.description) : "";
|
|
66
66
|
}
|
|
67
67
|
export function generateCard(repo, bannerUrl, config) {
|
|
68
|
-
const
|
|
69
|
-
const
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
if (
|
|
87
|
-
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
parts.push(`<p>${escapeHtml(overrides.personalNote)}</p>`);
|
|
92
|
-
}
|
|
93
|
-
// Key features (from config only)
|
|
94
|
-
if (overrides?.keyFeatures?.length) {
|
|
95
|
-
parts.push(`<p><strong>Key Features:</strong></p>`);
|
|
96
|
-
parts.push(`<ul>`);
|
|
97
|
-
for (const feature of overrides.keyFeatures) {
|
|
98
|
-
parts.push(`<li>${escapeHtml(feature)}</li>`);
|
|
99
|
-
}
|
|
100
|
-
parts.push(`</ul>`);
|
|
101
|
-
}
|
|
102
|
-
// Tech stack
|
|
103
|
-
const techStack = overrides?.techStack ?? inferTechStack(repo);
|
|
104
|
-
if (techStack) {
|
|
105
|
-
parts.push(`<p><strong>Tech Stack:</strong> ${escapeHtml(techStack)}</p>`);
|
|
106
|
-
}
|
|
107
|
-
// Separator
|
|
108
|
-
parts.push(`<hr>`);
|
|
109
|
-
return parts.join("\n");
|
|
68
|
+
const href = escapeHtml(repo.html_url);
|
|
69
|
+
const name = escapeHtml(repo.name);
|
|
70
|
+
const media = bannerUrl
|
|
71
|
+
? `<a class="pf-media" href="${href}" tabindex="-1" aria-hidden="true"><img src="${escapeHtml(bannerUrl)}" alt="" loading="lazy"></a>`
|
|
72
|
+
: `<a class="pf-media pf-tile" href="${href}" tabindex="-1" aria-hidden="true"><span>${name}</span></a>`;
|
|
73
|
+
const description = repoDescription(repo, config);
|
|
74
|
+
const site = siteLink(repo, config);
|
|
75
|
+
const lineParts = [];
|
|
76
|
+
if (description)
|
|
77
|
+
lineParts.push(escapeHtml(description));
|
|
78
|
+
if (site) {
|
|
79
|
+
lineParts.push(`<a href="${escapeHtml(site.url)}">${escapeHtml(site.label)}</a>`);
|
|
80
|
+
}
|
|
81
|
+
const lines = [
|
|
82
|
+
`<li class="pf-card">`,
|
|
83
|
+
` ${media}`,
|
|
84
|
+
` <div class="pf-head"><h3 class="pf-name"><a href="${href}">${name}</a></h3><span class="pf-stars">${formatStars(repo.stargazers_count)}</span></div>`,
|
|
85
|
+
];
|
|
86
|
+
if (lineParts.length > 0) {
|
|
87
|
+
lines.push(` <p class="pf-line">${lineParts.join(" ")}</p>`);
|
|
88
|
+
}
|
|
89
|
+
lines.push(`</li>`);
|
|
90
|
+
return lines.join("\n");
|
|
110
91
|
}
|
|
111
|
-
function
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
const topicLabels = techTopics.map((t) => t.charAt(0).toUpperCase() + t.slice(1));
|
|
138
|
-
const all = [...new Set([...languages, ...topicLabels])];
|
|
139
|
-
return all.length > 0 ? all.join(", ") : null;
|
|
92
|
+
function portfolioStyle(columns) {
|
|
93
|
+
const tablet = Math.min(columns, 2);
|
|
94
|
+
const rules = [
|
|
95
|
+
`.pf { --pf-muted: #999; --pf-rule: #3a3f4b; --pf-tile: #21252c; }`,
|
|
96
|
+
`.pf .pf-intro, .pf .pf-more { max-width: 720px; margin-left: auto; margin-right: auto; }`,
|
|
97
|
+
`.pf .pf-intro { margin-top: 0; margin-bottom: 0; font-size: 1.1em; line-height: 1.6; }`,
|
|
98
|
+
`.pf .pf-grid { display: grid; grid-template-columns: repeat(${columns}, minmax(0, 1fr)); gap: 3.2rem 2.4rem; margin: 2.4em 0 0; padding: 0; list-style: none; }`,
|
|
99
|
+
`.pf .pf-card { margin: 0; padding: 0; display: flex; flex-direction: column; min-width: 0; }`,
|
|
100
|
+
`.pf .pf-media { display: block; border-radius: 8px; overflow: hidden; aspect-ratio: 9 / 2; text-decoration: none; }`,
|
|
101
|
+
`.pf .pf-media img { display: block; width: 100%; height: 100%; object-fit: cover; object-position: center; margin: 0; border-radius: 0; }`,
|
|
102
|
+
`.pf .pf-tile { display: flex; align-items: center; padding: 0 7%; background: var(--pf-tile); border: 1px solid var(--pf-rule); }`,
|
|
103
|
+
`.pf .pf-tile span { color: #fff; font-size: 1.45em; font-weight: 700; letter-spacing: -.02em; line-height: 1.1; overflow-wrap: anywhere; }`,
|
|
104
|
+
`.pf .pf-head { display: flex; justify-content: space-between; align-items: baseline; gap: 1.2rem; margin: 1.2rem 0 0; }`,
|
|
105
|
+
`.pf .pf-name { margin: 0; min-width: 0; font-size: 1em; font-weight: 700; line-height: 1.3; letter-spacing: -.01em; overflow-wrap: anywhere; }`,
|
|
106
|
+
`.pf .pf-name a { color: #fff; text-decoration: none; }`,
|
|
107
|
+
`.pf .pf-name a:hover, .pf .pf-name a:focus-visible { text-decoration: underline; }`,
|
|
108
|
+
`.pf .pf-stars { flex: none; color: var(--pf-muted); font-size: .85em; font-variant-numeric: tabular-nums lining-nums; white-space: nowrap; }`,
|
|
109
|
+
`.pf .pf-line { margin: .4rem 0 0; font-size: .92em; line-height: 1.55; }`,
|
|
110
|
+
`.pf .pf-line a { white-space: nowrap; }`,
|
|
111
|
+
`.pf .pf-more { margin-top: 3.2em; margin-bottom: 0; color: var(--pf-muted); font-size: .95em; }`,
|
|
112
|
+
];
|
|
113
|
+
if (tablet < columns) {
|
|
114
|
+
rules.push(`@media (max-width: 1024px) {`, ` .pf .pf-grid.pf-cols-${columns} { grid-template-columns: repeat(${tablet}, minmax(0, 1fr)); }`, `}`);
|
|
115
|
+
}
|
|
116
|
+
rules.push(`@media (max-width: 640px) {`, ` .pf .pf-grid.pf-cols-${columns} { grid-template-columns: 1fr; gap: 2.8rem; }`, `}`);
|
|
117
|
+
return `<style>\n${rules.join("\n")}\n</style>`;
|
|
140
118
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
.slice(0, 8)
|
|
161
|
-
.map(([t]) => t.charAt(0).toUpperCase() + t.slice(1));
|
|
162
|
-
const lines = [];
|
|
163
|
-
lines.push(`<strong>Total Stars:</strong> ${totalStars}+`);
|
|
164
|
-
lines.push(`<strong>Public Repositories:</strong> ${totalRepos}`);
|
|
165
|
-
if (languages.length > 0) {
|
|
166
|
-
lines.push(`<strong>Primary Languages:</strong> ${languages.join(", ")}`);
|
|
167
|
-
}
|
|
168
|
-
if (focusAreas.length > 0) {
|
|
169
|
-
lines.push(`<strong>Focus Areas:</strong> ${focusAreas.join(", ")}`);
|
|
170
|
-
}
|
|
171
|
-
parts.push(`<p>${lines.join("<br>")}</p>`);
|
|
172
|
-
}
|
|
173
|
-
if (config.portfolio.footer.showViewAll) {
|
|
174
|
-
parts.push(`<p><a href="https://github.com/${config.github.username}?tab=repositories"><strong>View All Repositories →</strong></a></p>`);
|
|
175
|
-
}
|
|
119
|
+
/**
|
|
120
|
+
* The whole page as the HTML of one Ghost html card. The `kg-width-wide`
|
|
121
|
+
* class lets themes built on Ghost's content grid (Alto, Source, Casper)
|
|
122
|
+
* give the cards the wide column; the text stays at reading width.
|
|
123
|
+
*/
|
|
124
|
+
export function generatePortfolioHtml(input, config) {
|
|
125
|
+
const { columns, intro } = config.portfolio;
|
|
126
|
+
const user = escapeHtml(config.github.username);
|
|
127
|
+
const profile = `<a href="https://github.com/${user}">github.com/${user}</a>`;
|
|
128
|
+
const others = input.totalRepos - input.cards.length;
|
|
129
|
+
const more = others > 0
|
|
130
|
+
? `The other ${others.toLocaleString("en-US")} public ${others === 1 ? "repository is" : "repositories are"} on ${profile}.`
|
|
131
|
+
: `More on ${profile}.`;
|
|
132
|
+
const parts = [`<div class="pf kg-width-wide">`, portfolioStyle(columns)];
|
|
133
|
+
if (intro.trim()) {
|
|
134
|
+
parts.push(`<p class="pf-intro">${escapeHtml(intro.trim())}</p>`);
|
|
135
|
+
}
|
|
136
|
+
parts.push(`<ul class="pf-grid pf-cols-${columns}">`, ...input.cards, `</ul>`);
|
|
137
|
+
parts.push(`<p class="pf-more">${more}</p>`, `</div>`);
|
|
176
138
|
return parts.join("\n");
|
|
177
139
|
}
|
|
178
|
-
export function buildLexical(
|
|
179
|
-
const children = [];
|
|
180
|
-
// Opening separator
|
|
181
|
-
children.push({ type: "horizontalrule", version: 1 });
|
|
182
|
-
// Project cards
|
|
183
|
-
for (const card of cards) {
|
|
184
|
-
children.push({ type: "html", version: 1, html: card });
|
|
185
|
-
}
|
|
186
|
-
// Footer
|
|
187
|
-
if (footer) {
|
|
188
|
-
children.push({ type: "html", version: 1, html: footer });
|
|
189
|
-
}
|
|
140
|
+
export function buildLexical(html) {
|
|
190
141
|
return {
|
|
191
142
|
root: {
|
|
192
|
-
children,
|
|
143
|
+
children: [{ type: "html", version: 1, html }],
|
|
193
144
|
direction: "ltr",
|
|
194
145
|
format: "",
|
|
195
146
|
indent: 0,
|
|
@@ -198,6 +149,56 @@ export function buildLexical(cards, footer) {
|
|
|
198
149
|
},
|
|
199
150
|
};
|
|
200
151
|
}
|
|
152
|
+
/** The html card as Ghost renders it, with its begin and end markers. */
|
|
153
|
+
export function wrapHtmlCard(html) {
|
|
154
|
+
return `<!--kg-card-begin: html-->\n${html}\n<!--kg-card-end: html-->`;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* A standalone dark page that approximates Ghost's Alto theme in dark mode
|
|
158
|
+
* (content grid, widths, the .gh-content rules that reach into an html
|
|
159
|
+
* card), for checking the output in a browser before syncing.
|
|
160
|
+
*/
|
|
161
|
+
export function buildPreviewPage(html, title = "Portfolio") {
|
|
162
|
+
const t = escapeHtml(title);
|
|
163
|
+
return `<!doctype html>
|
|
164
|
+
<html lang="en">
|
|
165
|
+
<head>
|
|
166
|
+
<meta charset="utf-8">
|
|
167
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
168
|
+
<title>${t} (preview)</title>
|
|
169
|
+
<style>
|
|
170
|
+
:root { --ghost-accent-color: #FF1A75; --gap: 3.6rem; }
|
|
171
|
+
* { box-sizing: border-box; }
|
|
172
|
+
html { font-size: 62.5%; background: #282c35; }
|
|
173
|
+
body { margin: 0; background: #282c35; color: #ccc; font-family: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif; font-size: 1.6rem; line-height: 1.6; -webkit-font-smoothing: antialiased; }
|
|
174
|
+
.article-header { width: min(720px, 100% - var(--gap) * 2); margin: 60px auto 0; text-align: center; }
|
|
175
|
+
.post-title { margin: 0; color: #fff; font-size: 3.6rem; font-weight: 800; line-height: 1.15; letter-spacing: -.02em; }
|
|
176
|
+
.gh-canvas { --main: min(720px, 100% - var(--gap) * 2); --wide: minmax(0, calc((1200px - 720px) / 2)); --full: minmax(var(--gap), 1fr); display: grid; grid-template-columns: [full-start] var(--full) [wide-start] var(--wide) [main-start] var(--main) [main-end] var(--wide) [wide-end] var(--full) [full-end]; }
|
|
177
|
+
.gh-canvas > * { grid-column: main; }
|
|
178
|
+
.gh-canvas > .kg-width-wide { grid-column: wide; }
|
|
179
|
+
.gh-content { margin-top: 5.6rem; margin-bottom: 12rem; font-size: 1.7rem; word-break: break-word; }
|
|
180
|
+
.gh-content > * { margin-top: 0; margin-bottom: 0; }
|
|
181
|
+
.gh-content > * + * { margin-top: 1.6em; }
|
|
182
|
+
.gh-content h3 { font-size: 1.4em; }
|
|
183
|
+
.gh-content a { color: var(--ghost-accent-color); text-decoration: underline; word-break: break-word; }
|
|
184
|
+
.gh-content ul { padding-left: 2.8rem; }
|
|
185
|
+
.gh-content li + li { margin-top: .8rem; }
|
|
186
|
+
@media (max-width: 767px) { :root { --gap: 2rem; } .post-title { font-size: 3.2rem; } .article-header { margin-top: 40px; } .gh-content { margin-top: 4rem; } }
|
|
187
|
+
</style>
|
|
188
|
+
</head>
|
|
189
|
+
<body>
|
|
190
|
+
<main>
|
|
191
|
+
<article>
|
|
192
|
+
<header class="article-header"><h1 class="post-title">${t}</h1></header>
|
|
193
|
+
<div class="gh-content gh-canvas">
|
|
194
|
+
${wrapHtmlCard(html)}
|
|
195
|
+
</div>
|
|
196
|
+
</article>
|
|
197
|
+
</main>
|
|
198
|
+
</body>
|
|
199
|
+
</html>
|
|
200
|
+
`;
|
|
201
|
+
}
|
|
201
202
|
function escapeHtml(str) {
|
|
202
203
|
return str
|
|
203
204
|
.replace(/&/g, "&")
|
package/dist/generator.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generator.js","sourceRoot":"","sources":["../src/generator.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"generator.js","sourceRoot":"","sources":["../src/generator.ts"],"names":[],"mappings":"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"}
|
package/dist/ghost.d.ts
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import type { Config, LexicalDocument } from "./types.js";
|
|
2
2
|
export declare function generateJwt(apiKey: string): string;
|
|
3
|
+
/**
|
|
4
|
+
* Where Admin API requests go. Normally the public Ghost URL. With
|
|
5
|
+
* ghost.originUrl set they go straight to the origin (for example past a WAF
|
|
6
|
+
* that rejects page bodies containing <img>), carrying the public Host and
|
|
7
|
+
* X-Forwarded-Proto: https so Ghost answers as it would for the public site.
|
|
8
|
+
* The JWT audience stays /admin/ either way.
|
|
9
|
+
*/
|
|
10
|
+
export declare function adminUrl(config: Config, path: string): string;
|
|
3
11
|
interface GhostPage {
|
|
4
12
|
id: string;
|
|
5
13
|
updated_at: string;
|
package/dist/ghost.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ghost.d.ts","sourceRoot":"","sources":["../src/ghost.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAG1D,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CA6BlD;
|
|
1
|
+
{"version":3,"file":"ghost.d.ts","sourceRoot":"","sources":["../src/ghost.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAG1D,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CA6BlD;AAgBD;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAG7D;AAkBD,UAAU,SAAS;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAelE;AAED,wBAAsB,UAAU,CAC9B,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,SAAS,CAAC,CAwBpB"}
|
package/dist/ghost.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createHmac } from "node:crypto";
|
|
2
|
-
import { fetchWithRetry } from "./http.js";
|
|
2
|
+
import { fetchWithRetry, rawRequest } from "./http.js";
|
|
3
3
|
export function generateJwt(apiKey) {
|
|
4
4
|
const [keyId, secretHex] = apiKey.split(":");
|
|
5
5
|
if (!keyId || !secretHex) {
|
|
@@ -19,25 +19,43 @@ export function generateJwt(apiKey) {
|
|
|
19
19
|
.replace(/=+$/, "");
|
|
20
20
|
return `${header}.${payload}.${signature}`;
|
|
21
21
|
}
|
|
22
|
-
function ghostHeaders(
|
|
23
|
-
|
|
24
|
-
Authorization: `Ghost ${generateJwt(
|
|
22
|
+
function ghostHeaders(config) {
|
|
23
|
+
const h = {
|
|
24
|
+
Authorization: `Ghost ${generateJwt(config.ghost.adminApiKey)}`,
|
|
25
25
|
"Content-Type": "application/json",
|
|
26
26
|
"User-Agent": "Mozilla/5.0 ghost-github-portfolio",
|
|
27
27
|
};
|
|
28
|
+
if (config.ghost.originUrl) {
|
|
29
|
+
const publicUrl = new URL(config.ghost.url);
|
|
30
|
+
h.Host = config.ghost.hostHeader || publicUrl.host;
|
|
31
|
+
h["X-Forwarded-Proto"] = "https";
|
|
32
|
+
}
|
|
33
|
+
return h;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Where Admin API requests go. Normally the public Ghost URL. With
|
|
37
|
+
* ghost.originUrl set they go straight to the origin (for example past a WAF
|
|
38
|
+
* that rejects page bodies containing <img>), carrying the public Host and
|
|
39
|
+
* X-Forwarded-Proto: https so Ghost answers as it would for the public site.
|
|
40
|
+
* The JWT audience stays /admin/ either way.
|
|
41
|
+
*/
|
|
42
|
+
export function adminUrl(config, path) {
|
|
43
|
+
const base = config.ghost.originUrl || config.ghost.url;
|
|
44
|
+
return `${base}/ghost/api/admin/${path}`;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Send an Admin API request. With originUrl set it goes through rawRequest,
|
|
48
|
+
* because the global fetch drops a custom Host header.
|
|
49
|
+
*/
|
|
50
|
+
function send(config, url, init = {}) {
|
|
51
|
+
return fetchWithRetry(url, { ...init, headers: ghostHeaders(config) }, config.ghost.originUrl ? { fetchImpl: rawRequest } : undefined);
|
|
28
52
|
}
|
|
29
53
|
export async function fetchPage(config) {
|
|
30
|
-
const {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
else {
|
|
36
|
-
endpoint = `${url}/ghost/api/admin/pages/slug/${pageSlug}/`;
|
|
37
|
-
}
|
|
38
|
-
const res = await fetchWithRetry(endpoint, {
|
|
39
|
-
headers: ghostHeaders(adminApiKey),
|
|
40
|
-
});
|
|
54
|
+
const { pageId, pageSlug } = config.ghost;
|
|
55
|
+
const endpoint = pageId
|
|
56
|
+
? adminUrl(config, `pages/${pageId}/`)
|
|
57
|
+
: adminUrl(config, `pages/slug/${pageSlug}/`);
|
|
58
|
+
const res = await send(config, endpoint);
|
|
41
59
|
if (!res.ok) {
|
|
42
60
|
const body = await res.text();
|
|
43
61
|
throw new Error(`Ghost API error ${res.status}: ${body}`);
|
|
@@ -46,8 +64,7 @@ export async function fetchPage(config) {
|
|
|
46
64
|
return data.pages[0];
|
|
47
65
|
}
|
|
48
66
|
export async function updatePage(config, pageId, updatedAt, lexical) {
|
|
49
|
-
const
|
|
50
|
-
const endpoint = `${url}/ghost/api/admin/pages/${pageId}/`;
|
|
67
|
+
const endpoint = adminUrl(config, `pages/${pageId}/`);
|
|
51
68
|
const body = {
|
|
52
69
|
pages: [
|
|
53
70
|
{
|
|
@@ -56,9 +73,8 @@ export async function updatePage(config, pageId, updatedAt, lexical) {
|
|
|
56
73
|
},
|
|
57
74
|
],
|
|
58
75
|
};
|
|
59
|
-
const res = await
|
|
76
|
+
const res = await send(config, endpoint, {
|
|
60
77
|
method: "PUT",
|
|
61
|
-
headers: ghostHeaders(adminApiKey),
|
|
62
78
|
body: JSON.stringify(body),
|
|
63
79
|
});
|
|
64
80
|
if (!res.ok) {
|
package/dist/ghost.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ghost.js","sourceRoot":"","sources":["../src/ghost.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"ghost.js","sourceRoot":"","sources":["../src/ghost.ts"],"names":[],"mappings":"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"}
|
package/dist/github.d.ts
CHANGED
|
@@ -1,10 +1,29 @@
|
|
|
1
1
|
import type { Config, GitHubRepo, RepoOverride } from "./types.js";
|
|
2
|
-
|
|
3
|
-
export
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
/** Fetch every public repo the user owns, unfiltered. */
|
|
3
|
+
export declare function fetchAllRepos(config: Config, verbose?: boolean): Promise<GitHubRepo[]>;
|
|
4
|
+
export declare function isAwesomeList(repo: GitHubRepo): boolean;
|
|
5
|
+
/**
|
|
6
|
+
* Apply every filter, sort by stars and only then cut to maxRepos, so an
|
|
7
|
+
* excluded repo never takes a slot.
|
|
8
|
+
*/
|
|
9
|
+
export declare function selectRepos(repos: GitHubRepo[], config: Config): GitHubRepo[];
|
|
10
|
+
export interface BannerResult {
|
|
11
|
+
/** URL to use in the card, or null to draw a name tile. */
|
|
12
|
+
url: string | null;
|
|
13
|
+
/** Why no banner is used, one entry per path tried. Empty when url is set or banners are off. */
|
|
14
|
+
problems: string[];
|
|
6
15
|
}
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
16
|
+
/**
|
|
17
|
+
* List every reference in an SVG that a browser would have to load from
|
|
18
|
+
* somewhere else. Inside an `<img>` those are never loaded, so the banner
|
|
19
|
+
* renders blank or broken. `#id` and `data:` references are fine.
|
|
20
|
+
*/
|
|
21
|
+
export declare function findExternalRefs(svg: string): string[];
|
|
22
|
+
/**
|
|
23
|
+
* Pick the banner for a repo: the per-repo path first (repos.<name>.bannerPath
|
|
24
|
+
* or bannerPaths.<name>), then defaultBannerPath. A banner is used only when
|
|
25
|
+
* it answers 200 and, for SVG, loads nothing from outside itself.
|
|
26
|
+
*/
|
|
27
|
+
export declare function resolveBanner(repo: GitHubRepo, config: Config): Promise<BannerResult>;
|
|
28
|
+
export declare function fetchPortfolioConfig(repo: GitHubRepo): Promise<RepoOverride | null>;
|
|
10
29
|
//# sourceMappingURL=github.d.ts.map
|
package/dist/github.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"github.d.ts","sourceRoot":"","sources":["../src/github.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"github.d.ts","sourceRoot":"","sources":["../src/github.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAkBnE,yDAAyD;AACzD,wBAAsB,aAAa,CACjC,MAAM,EAAE,MAAM,EACd,OAAO,UAAQ,GACd,OAAO,CAAC,UAAU,EAAE,CAAC,CA+CvB;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAKvD;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,UAAU,EAAE,CAsB7E;AAED,MAAM,WAAW,YAAY;IAC3B,2DAA2D;IAC3D,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,iGAAiG;IACjG,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CActD;AAgCD;;;;GAIG;AACH,wBAAsB,aAAa,CACjC,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,YAAY,CAAC,CAuBvB;AAID,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,UAAU,GACf,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAuB9B"}
|