Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Prerender sponsors for READMEs #638

Merged
merged 12 commits into from
Oct 4, 2024
Merged
5 changes: 5 additions & 0 deletions .github/workflows/data-fetch.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ jobs:
env:
ESLINT_GITHUB_TOKEN: ${{ secrets.DATA_FETCH_TOKEN }}

- name: Generate sponsor data
run: npm run build:sponsors
env:
ESLINT_GITHUB_TOKEN: ${{ secrets.DATA_FETCH_TOKEN }}
amareshsm marked this conversation as resolved.
Show resolved Hide resolved

- name: Setup Git
run: |
git config user.name "GitHub Actions Bot"
Expand Down
2 changes: 1 addition & 1 deletion eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ module.exports = [
},

{
files: ["tools/**/*.js"],
files: ["tools/**/*.js", "includes/*.js"],
rules: {
"no-console": "off",
"n/no-process-exit": "off",
Expand Down
169 changes: 169 additions & 0 deletions includes/generate-sponsors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
/**
* @fileoverview
* This script generates a markdown file containing sponsor information.
* It fetches sponsor data from specified JSON files, formats
* the data into HTML sections, and writes the output to a new markdown file.
*
* Usage:
* Run the script using Node.js:
* node tools/generate-sponsors.js
*
* Author: Amaresh S M
*/
"use strict";

//-----------------------------------------------------------------------------
// Requirements
//-----------------------------------------------------------------------------

const fs = require("node:fs").promises;
const path = require("node:path");
const { stripIndents } = require("common-tags");

//-----------------------------------------------------------------------------
// Data
//-----------------------------------------------------------------------------

const SPONSORS_URL = path.resolve(__dirname, "../src/_data/sponsors.json");
const TECH_SPONSORS_URL = path.resolve(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we rename these to SPONSORS_FILE_PATH and TECH_SPONSORS_FILE_PATH as these are not URLs but file paths? Also, can we rename fetch* functions to read* as they're not fetching data from URLs but reading files?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure

__dirname,
"../src/_data/techsponsors.json",
);
const NEW_FILE_PATH = path.resolve(__dirname, "../sponsors/sponsors.md");

const TECH_SPONSORS_IMAGE_PATH =
"https://raw.githubusercontent.com/eslint/eslint.org/main/src";

const HEIGHTS = {
diamond: 160,
platinum: 128,
gold: 96,
silver: 64,
bronze: 32,
};

const SPONSOR_INTRO_TEXT = `## Sponsors

The following companies, organizations, and individuals support ESLint's ongoing maintenance and development. [Become a Sponsor](https://eslint.org/donate)
to get your logo on our READMEs and [website](https://eslint.org/sponsors).`;

//-----------------------------------------------------------------------------
// Helpers
//-----------------------------------------------------------------------------

/**
* Fetches data from the specified URL and parses it as JSON.
* @param {string} url The URL to fetch data from.
* @returns {Object|null} The parsed data object, or null if an error occurs.
*/
async function fetchData(url) {
try {
const data = await fs.readFile(url, "utf8");
return JSON.parse(data);
} catch (err) {
console.error("Error reading or parsing the file:", err);
return null;
}
}

/**
* Fetches the latest sponsors data from the website.
* @returns {Object|null} The sponsors data object without backers.
*/
async function fetchSponsorsData() {
const sponsorsData = await fetchData(SPONSORS_URL);
if (sponsorsData) {
delete sponsorsData.backers;
}
return sponsorsData;
}

/**
* Fetches the latest tech sponsors data from the website.
* @returns {Array<Object>|null} The tech sponsors data object.
*/
async function fetchTechSponsors() {
return fetchData(TECH_SPONSORS_URL);
}

/**
* Formats an array of sponsors into HTML for the readme.
* @param {Array} sponsors The array of sponsors.
* @returns {string} The HTML for the sponsors section.
*/
function formatSponsors(sponsors) {
const nonEmptySponsors = Object.keys(sponsors).filter(
tier => sponsors[tier].length,
);

return stripIndents`
${nonEmptySponsors
.map(
tier => `<h3>${tier[0].toUpperCase()}${tier.slice(
1,
)} Sponsors</h3>
<p>${sponsors[tier]
.map(
sponsor =>
`<a href="${sponsor.url || "#"}"><img src="${
sponsor.image
}" alt="${sponsor.name}" height="${HEIGHTS[tier]}"></a>`,
)
.join(" ")}</p>`,
)
.join("")}`;
}

/**
* Formats an array of tech sponsors into HTML for the new file.
* @param {Array} sponsors The array of tech sponsors.
* @returns {string} The HTML for the tech sponsors section.
*/
function formatTechSponsors(sponsors) {
return stripIndents`
<h3>Technology Sponsors</h3>
Technology sponsors allow us to use their products and services for free as part of a contribution to the open source ecosystem and our work.
<p>${sponsors
.map(
sponsor =>
`<a href="${sponsor.url || "#"}"><img src="${
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are sponsors for whom image might not exist as well. We might want to handle that? In the PR generated sponsors.md I was able to see one sponsor without image?
Screenshot 2024-10-01 at 11 39 20 PM

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I checked, I didn’t see a broken image icon. I will look it in this again.

image

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logo isn't missing on the website, and that's using the same data, so I'm not sure why this is creating a missing image. 🤔

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps you were using an old data file. Rebasing this branch and running the script again to regenerate sponsors.md might fix this.

TECH_SPONSORS_IMAGE_PATH + sponsor.image
}" alt="${sponsor.name}" height="${HEIGHTS.bronze}"></a>`,
)
.join(" ")}</p>`;
}

//-----------------------------------------------------------------------------
// Main
//-----------------------------------------------------------------------------

/**
* Writes data to a specified file.
* @param {string} filePath The path to the file where data should be written.
* @param {string|Object} data The data to write to the file. If an object is provided, it should be stringified.
* @returns {Promise<void>} A promise that resolves when the write operation is complete.
* @throws {Error} Will log an error if writing to the file fails.
*/
async function writeData(filePath, data) {
try {
await fs.writeFile(filePath, data, "utf8");
} catch (err) {
console.error("Error writing data:", err);
}
}

(async () => {
const [allSponsors, techSponsors] = await Promise.all([
fetchSponsorsData(),
fetchTechSponsors(),
]);

const newFileContent = stripIndents`
${SPONSOR_INTRO_TEXT}
${formatSponsors(allSponsors)}
${formatTechSponsors(techSponsors)}
`;

await writeData(NEW_FILE_PATH, newFileContent);
console.log(`Sponsors information has been written to ${NEW_FILE_PATH}`);
})();
85 changes: 39 additions & 46 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"build:playground:watch": "webpack watch --mode=development",
"build:sass": "sass --no-source-map --style=compressed src/assets/scss:src/assets/css",
"build:sass:watch": "sass --watch src/assets/scss:src/assets/css",
"build:sponsors": "node includes/generate-sponsors.js",
"build:website": "npx @11ty/eleventy",
"build:website:watch": "cross-env IS_DEV=true eleventy --serve --port=2022",
"fetch": "npm-run-all --parallel fetch:*",
Expand Down Expand Up @@ -71,6 +72,7 @@
"@octokit/graphql": "^4.8.0",
"@octokit/rest": "^18.12.0",
"babel-loader": "^8.2.3",
"common-tags": "^1.8.2",
"cheerio": "*",
"cross-env": "^7.0.3",
"css-loader": "^6.7.1",
Expand Down
12 changes: 12 additions & 0 deletions sponsors/sponsors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
## Sponsors

The following companies, organizations, and individuals support ESLint's ongoing maintenance and development. [Become a Sponsor](https://eslint.org/donate)
to get your logo on our READMEs and [website](https://eslint.org/sponsors).
<h3>Platinum Sponsors</h3>
<p><a href="https://automattic.com"><img src="https://images.opencollective.com/automattic/d0ef3e1/logo.png" alt="Automattic" height="128"></a> <a href="https://www.airbnb.com/"><img src="https://images.opencollective.com/airbnb/d327d66/logo.png" alt="Airbnb" height="128"></a></p><h3>Gold Sponsors</h3>
<p><a href="#"><img src="https://images.opencollective.com/guest-bf377e88/avatar.png" alt="Eli Schleifer" height="96"></a> <a href="https://opensource.siemens.com"><img src="https://avatars.githubusercontent.com/u/624020?v=4" alt="Siemens" height="96"></a></p><h3>Silver Sponsors</h3>
<p><a href="https://www.jetbrains.com/"><img src="https://images.opencollective.com/jetbrains/fe76f99/logo.png" alt="JetBrains" height="64"></a> <a href="https://liftoff.io/"><img src="https://images.opencollective.com/liftoff/5c4fa84/logo.png" alt="Liftoff" height="64"></a> <a href="https://americanexpress.io"><img src="https://avatars.githubusercontent.com/u/3853301?v=4" alt="American Express" height="64"></a> <a href="https://www.workleap.com"><img src="https://avatars.githubusercontent.com/u/53535748?u=d1e55d7661d724bf2281c1bfd33cb8f99fe2465f&v=4" alt="Workleap" height="64"></a></p><h3>Bronze Sponsors</h3>
<p><a href="https://www.notion.so"><img src="https://images.opencollective.com/notion/bf3b117/logo.png" alt="notion" height="32"></a> <a href="https://www.crosswordsolver.org/anagram-solver/"><img src="https://images.opencollective.com/anagram-solver/2666271/logo.png" alt="Anagram Solver" height="32"></a> <a href="https://icons8.com/"><img src="https://images.opencollective.com/icons8/7fa1641/logo.png" alt="Icons8" height="32"></a> <a href="https://discord.com"><img src="https://images.opencollective.com/discordapp/f9645d9/logo.png" alt="Discord" height="32"></a> <a href="https://www.ignitionapp.com"><img src="https://avatars.githubusercontent.com/u/5753491?v=4" alt="Ignition" height="32"></a> <a href="https://nx.dev"><img src="https://avatars.githubusercontent.com/u/23692104?v=4" alt="Nx" height="32"></a> <a href="https://herocoders.com"><img src="https://avatars.githubusercontent.com/u/37549774?v=4" alt="HeroCoders" height="32"></a> <a href="https://usenextbase.com"><img src="https://avatars.githubusercontent.com/u/145838380?v=4" alt="Nextbase Starter Kit" height="32"></a></p>
<h3>Technology Sponsors</h3>
Technology sponsors allow us to use their products and services for free as part of a contribution to the open source ecosystem and our work.
<p><a href="https://netlify.com"><img src="https://raw.githubusercontent.com/eslint/eslint.org/main/src/assets/images/techsponsors/netlify-icon.svg" alt="Netlify" height="32"></a> <a href="https://algolia.com"><img src="https://raw.githubusercontent.com/eslint/eslint.org/main/src/assets/images/techsponsors/algolia-icon.svg" alt="Algolia" height="32"></a> <a href="https://1password.com"><img src="https://raw.githubusercontent.com/eslint/eslint.org/main/src/assets/images/techsponsors/1password-icon.svg" alt="1Password" height="32"></a></p>
2 changes: 1 addition & 1 deletion tools/commit-data.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ else
echo "Data changed!"

# commit the result
git add src/_data/
git add src/_data/ sponsors/
git commit -m "chore: Update remote data"

# push back to source control
Expand Down