Skip to content

Commit 93c7381

Browse files
authored
Merge pull request graphql#936 from ardatan/redesign-code
[Gatsby] New Code Page
2 parents 21f8ab2 + 99aedfa commit 93c7381

File tree

133 files changed

+2486
-821
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

133 files changed

+2486
-821
lines changed

gatsby-node.js

Lines changed: 115 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,120 @@
11
const path = require("path")
2+
const sortLibs = require("./scripts/sort-libraries")
3+
const globby = require('globby');
4+
const frontmatterParser = require('parser-front-matter');
5+
const { readFile } = require("fs-extra");
6+
const { promisify } = require('util');
27

38
exports.onCreatePage = async ({ page, actions }) => {
49
const { createPage, deletePage } = actions
510
deletePage(page)
11+
let context = {
12+
...page.context,
13+
sourcePath: path.relative(__dirname, page.componentPath),
14+
}
15+
if (page.path === "/code" || page.path === "/code/") {
16+
const markdownFilePaths = await globby('src/content/code/**/*.md');
17+
const codeData = {}
18+
const slugMap = require('./src/content/code/slug-map.json');
19+
const parse$ = promisify(frontmatterParser.parse);
20+
await Promise.all(markdownFilePaths.map(async markdownFilePath => {
21+
22+
const markdownFileContent = await readFile(markdownFilePath, "utf-8")
23+
let {
24+
data: { name, description, url, github, npm, gem },
25+
content: howto,
26+
} = await parse$(markdownFileContent, undefined)
27+
howto = howto.trim();
28+
const pathArr = markdownFilePath.split("/")
29+
if (markdownFilePath.includes("language-support")) {
30+
const languageSupportDirIndex = pathArr.indexOf("language-support")
31+
const languageNameSlugIndex = languageSupportDirIndex + 1
32+
const languageNameSlug = pathArr[languageNameSlugIndex]
33+
const languageName = slugMap[languageNameSlug]
34+
codeData.Languages = codeData.Languages || {}
35+
codeData.Languages[languageName] =
36+
codeData.Languages[languageName] || {}
37+
38+
const categoryNameSlugIndex = languageSupportDirIndex + 2
39+
const categoryNameSlug = pathArr[categoryNameSlugIndex]
40+
const categoryName = slugMap[categoryNameSlug]
41+
codeData.Languages[languageName][categoryName] =
42+
codeData.Languages[languageName][categoryName] || []
43+
codeData.Languages[languageName][categoryName].push({
44+
name,
45+
description,
46+
howto,
47+
url,
48+
github,
49+
npm,
50+
gem,
51+
sourcePath: markdownFilePath,
52+
})
53+
} else {
54+
const codeDirIndex = pathArr.indexOf("code")
55+
const categoryNameSlugIndex = codeDirIndex + 1
56+
const categoryNameSlug = pathArr[categoryNameSlugIndex]
57+
const categoryName = slugMap[categoryNameSlug]
58+
codeData[categoryName] = codeData[categoryName] || []
59+
codeData[categoryName].push({
60+
name,
61+
description,
62+
howto,
63+
url,
64+
github,
65+
npm,
66+
gem,
67+
sourcePath: markdownFilePath,
68+
})
69+
}
70+
}))
71+
const languageList = []
72+
let sortedTools = []
73+
await Promise.all([
74+
Promise.all(
75+
Object.keys(codeData.Languages).map(async languageName => {
76+
const libraryCategoryMap = codeData.Languages[languageName]
77+
let languageTotalStars = 0
78+
await Promise.all(
79+
Object.keys(libraryCategoryMap).map(async libraryCategoryName => {
80+
const libraries = libraryCategoryMap[libraryCategoryName]
81+
const { sortedLibs, totalStars } = await sortLibs(libraries)
82+
libraryCategoryMap[libraryCategoryName] = sortedLibs
83+
languageTotalStars += totalStars || 0
84+
})
85+
)
86+
languageList.push({
87+
name: languageName,
88+
totalStars: languageTotalStars,
89+
categoryMap: libraryCategoryMap,
90+
})
91+
})
92+
),
93+
sortLibs(codeData.Tools).then(({ sortedLibs }) => {
94+
sortedTools = sortedLibs
95+
}),
96+
])
97+
98+
context = {
99+
...context,
100+
otherLibraries: {
101+
Services: codeData.Services,
102+
Tools: sortedTools,
103+
"More Stuff": codeData["More Stuff"],
104+
},
105+
languageList: languageList.sort((a, b) => {
106+
if (a.totalStars > b.totalStars) {
107+
return -1
108+
} else if (a.totalStars < b.totalStars) {
109+
return 1
110+
}
111+
return 0
112+
}),
113+
}
114+
}
6115
createPage({
7116
...page,
8-
context: {
9-
...page.context,
10-
sourcePath: path.relative(__dirname, page.componentPath),
11-
},
117+
context,
12118
})
13119
}
14120

@@ -64,7 +170,10 @@ exports.createPages = async ({ graphql, actions }) => {
64170
parent: { relativeDirectory, sourceInstanceName },
65171
} = node
66172

67-
if (sourceInstanceName !== "content") {
173+
if (
174+
sourceInstanceName !== "content" ||
175+
relativeDirectory.includes("code")
176+
) {
68177
return
69178
}
70179

@@ -176,7 +285,7 @@ exports.createPages = async ({ graphql, actions }) => {
176285
categoriesMap[currentCategory.name] = currentCategory
177286
}
178287

179-
sideBardata[folder] = Object.values(categoriesMap);
288+
sideBardata[folder] = Object.values(categoriesMap)
180289
})
181290
)
182291

package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,17 @@
2424
"gatsby-plugin-webfonts": "1.1.3",
2525
"gatsby-source-filesystem": "2.4.0",
2626
"gatsby-transformer-remark": "2.9.0",
27+
"globby": "11.0.1",
2728
"graphql": "15.4.0",
2829
"marked": "1.2.2",
30+
"numbro": "2.3.2",
31+
"parser-front-matter": "1.6.4",
2932
"prism-react-renderer": "1.1.1",
3033
"prismjs": "1.22.0",
3134
"react": "17.0.1",
3235
"react-dom": "17.0.1",
33-
"react-helmet": "6.1.0"
36+
"react-helmet": "6.1.0",
37+
"timeago.js": "4.0.2"
3438
},
3539
"devDependencies": {
3640
"@types/codemirror": "0.0.98",

scripts/sort-libraries.js

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
const fetch = require(`node-fetch`);
2+
const numbro = require("numbro");
3+
const timeago = require('timeago.js');
4+
5+
const getGitHubStats = async githubRepo => {
6+
const [owner, repoName] = githubRepo.split("/")
7+
const accessToken = process.env.GITHUB_ACCESS_TOKEN
8+
if (!accessToken) {
9+
return {};
10+
}
11+
const query = /* GraphQL */ `
12+
fragment defaultBranchRefFragment on Ref {
13+
target {
14+
... on Commit {
15+
history(since: $since) {
16+
edges {
17+
node {
18+
author {
19+
name
20+
}
21+
pushedDate
22+
}
23+
}
24+
}
25+
}
26+
}
27+
}
28+
query($owner: String!, $repoName: String!, $since: GitTimestamp!) {
29+
repositoryOwner(login: $owner) {
30+
repository(name: $repoName) {
31+
defaultBranchRef {
32+
...defaultBranchRefFragment
33+
}
34+
stargazers {
35+
totalCount
36+
}
37+
updatedAt
38+
forkCount
39+
pullRequests {
40+
totalCount
41+
}
42+
description
43+
licenseInfo {
44+
name
45+
}
46+
releases(last: 1) {
47+
nodes {
48+
publishedAt
49+
}
50+
}
51+
tags: refs(refPrefix: "refs/tags/", first: 1, orderBy: {field: TAG_COMMIT_DATE, direction: DESC}) {
52+
nodes {
53+
name
54+
target {
55+
... on Tag {
56+
target {
57+
... on Commit {
58+
pushedDate
59+
}
60+
}
61+
}
62+
}
63+
}
64+
}
65+
}
66+
}
67+
}
68+
`
69+
const lastMonth = new Date()
70+
lastMonth.setMonth(lastMonth.getMonth() - 3)
71+
const response = await fetch("https://api.github.com/graphql", {
72+
method: "POST",
73+
body: JSON.stringify({
74+
query,
75+
variables: { owner, repoName, since: lastMonth },
76+
}),
77+
headers: {
78+
Authorization: `Bearer ${accessToken}`,
79+
"Content-Type": "application/json",
80+
},
81+
})
82+
const responseJson = await response.json()
83+
if (responseJson && responseJson.errors) {
84+
throw JSON.stringify(responseJson.errors);
85+
}
86+
if (!responseJson || !responseJson.data) {
87+
throw `GitHub returned empty response for ${owner}/${repoName}`
88+
}
89+
const { repositoryOwner } = responseJson.data
90+
if (!repositoryOwner) {
91+
throw `No GitHub user found for ${owner}/${repoName}`
92+
}
93+
const { repository: repo } = repositoryOwner
94+
if (!repo) {
95+
throw `No GitHub repo found ${owner}/${repoName}`
96+
}
97+
const stars = repo.stargazers.totalCount
98+
const commitHistory = repo.defaultBranchRef.target.history.edges
99+
100+
let hasCommitsInLast3Months = false;
101+
commitHistory.forEach(commit => {
102+
if (!commit.node.author.name.match(/bot/i)) {
103+
hasCommitsInLast3Months = true;
104+
}
105+
})
106+
const formattedStars = numbro(stars).format({
107+
average: true,
108+
});
109+
110+
const releases = [];
111+
if (repo.tags && repo.tags.nodes && repo.tags.nodes.length && repo.tags.nodes[0].target.target && repo.tags.nodes[0].target.target.pushedDate) {
112+
releases.push(repo.tags.nodes[0].target.target.pushedDate);
113+
}
114+
if (repo.releases && repo.releases.nodes && repo.releases.nodes.length) {
115+
releases.push(repo.releases.nodes[0].publishedAt)
116+
}
117+
if(owner.includes("graphql")) {
118+
console.log({ releases, repoName })
119+
}
120+
121+
const lastRelease = releases.filter(Boolean).sort().reverse()[0]
122+
123+
return {
124+
hasCommitsInLast3Months,
125+
stars,
126+
formattedStars,
127+
license: repo.licenseInfo && repo.licenseInfo.name,
128+
lastRelease,
129+
formattedLastRelease: lastRelease && timeago.format(lastRelease),
130+
}
131+
}
132+
133+
const getNpmStats = async packageName => {
134+
const response = await fetch(
135+
`https://api.npmjs.org/downloads/point/last-week/${encodeURIComponent(
136+
packageName
137+
)}`
138+
)
139+
const responseJson = await response.json()
140+
const downloadCount = responseJson.downloads
141+
return { downloadCount }
142+
}
143+
144+
const getGemStats = async packageName => {
145+
const response = await fetch(
146+
`https://rubygems.org/api/v1/gems/${encodeURIComponent(packageName)}.json`
147+
)
148+
const responseJson = await response.json()
149+
const downloadCount = responseJson.downloads
150+
return { downloadCount }
151+
}
152+
153+
const sortLibs = async libs => {
154+
let totalStars = 0;
155+
const libsWithScores = await Promise.all(
156+
libs.map(async lib => {
157+
const [
158+
npmStats = {},
159+
gemStars = {},
160+
githubStats = {},
161+
] = await Promise.all([
162+
lib.npm && getNpmStats(lib.npm),
163+
lib.gem && getGemStats(lib.gem),
164+
lib.github && getGitHubStats(lib.github),
165+
])
166+
const result = {
167+
...lib,
168+
...npmStats,
169+
...gemStars,
170+
...githubStats,
171+
}
172+
totalStars += result.stars || 0;
173+
return result;
174+
})
175+
)
176+
const sortedLibs = libsWithScores.sort((a, b) => {
177+
let aScore = 0,
178+
bScore = 0
179+
if ("downloadCount" in a && 'downloadCount' in b) {
180+
if (a.downloadCount > b.downloadCount) {
181+
aScore += 40
182+
} else if (b.downloadCount > a.downloadCount) {
183+
bScore += 40
184+
}
185+
}
186+
if ("hasCommitsInLast3Months" in a && a.hasCommitsInLast3Months) {
187+
aScore += 30
188+
}
189+
if ("hasCommitsInLast3Months" in b && b.hasCommitsInLast3Months) {
190+
bScore += 30
191+
}
192+
if ('stars' in a && 'stars' in b) {
193+
if (a.stars > b.stars) {
194+
aScore += 40
195+
} else if (a.stars < b.stars) {
196+
bScore += 40
197+
}
198+
}
199+
if (bScore > aScore) {
200+
return 1
201+
} else if (bScore < aScore) {
202+
return -1
203+
}
204+
return 0
205+
})
206+
return { sortedLibs, totalStars }
207+
}
208+
209+
module.exports = sortLibs

0 commit comments

Comments
 (0)