2020-05-21 23:48:06 +02:00
|
|
|
// Build tool for generating README.md
|
|
|
|
|
2021-08-09 21:13:30 +02:00
|
|
|
const { EOL } = require('os');
|
2020-05-21 23:48:06 +02:00
|
|
|
const path = require('path');
|
|
|
|
const fs = require('fs-extra');
|
2020-06-01 23:43:40 +02:00
|
|
|
const moment = require('moment');
|
2020-05-21 23:48:06 +02:00
|
|
|
const YAML = require('yaml');
|
|
|
|
|
2020-05-22 04:29:03 +02:00
|
|
|
const BUILD_SECTION = {
|
2020-06-01 23:57:08 +02:00
|
|
|
header: () => readFile('md/_header.md').replace('{{DATE}}', moment().format('MMMM Do YYYY').replace(/ /g, '%20')),
|
2020-05-24 02:33:07 +02:00
|
|
|
index: () => readFile('md/_index.md'),
|
|
|
|
contributing: () => readFile('md/_contributing.md'),
|
2020-06-01 18:42:12 +02:00
|
|
|
browserExtensions: () => generateBrowserExtensions(),
|
2020-05-24 02:33:07 +02:00
|
|
|
disclaimer: () => readFile('md/_disclaimer.md'),
|
|
|
|
webBasedProducts: () => generateCategorySection('Web-based products', readYaml()['web based products']),
|
|
|
|
operatingSystems: () => generateCategorySection('Operating systems', readYaml()['operating systems']),
|
|
|
|
desktopApps: () => generateCategorySection('Desktop apps', readYaml()['desktop applications']),
|
2020-05-25 17:46:50 +02:00
|
|
|
mobileApps: () => generateCategorySection('Mobile apps', readYaml()['mobile applications']),
|
2020-05-24 02:33:07 +02:00
|
|
|
hardware: () => generateCategorySection('Hardware', readYaml()['hardware']),
|
|
|
|
useful: () => '# Useful links, tools, and advice',
|
|
|
|
resources: () => readFile('md/_resources.md'),
|
2020-06-01 20:31:52 +02:00
|
|
|
books: () => generatePublications('Books', 'books'),
|
|
|
|
blogs: () => generatePublications('Blog posts', 'blogs'),
|
|
|
|
news: () => generatePublications('News articles', 'news'),
|
2020-05-24 02:33:07 +02:00
|
|
|
lighterSide: () => readFile('md/_lighterSide.md'),
|
|
|
|
closingRemarks: () => readFile('md/_closingRemarks.md')
|
2021-07-06 05:08:43 +02:00
|
|
|
};
|
2020-05-22 04:29:03 +02:00
|
|
|
|
2020-06-01 17:14:27 +02:00
|
|
|
// Button that brings the user to the top of the page
|
2020-06-01 23:44:10 +02:00
|
|
|
const BACK_TO_TOP = '[![Back to top](https://img.shields.io/badge/Back%20to%20top-lightgrey?style=flat-square)](#index)';
|
2020-06-01 17:14:27 +02:00
|
|
|
|
2020-05-22 04:29:03 +02:00
|
|
|
/**
|
|
|
|
* Synchronously reads a file using fs-extra and path.join()
|
|
|
|
* @param {String} filename The file to read
|
|
|
|
*/
|
|
|
|
function readFile(filename) {
|
|
|
|
return fs.readFileSync(path.join(__dirname, filename)).toString();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reads degoogle.yml
|
|
|
|
*/
|
|
|
|
function readYaml() {
|
2020-06-01 18:16:45 +02:00
|
|
|
return YAML.parse(fs.readFileSync(path.join(__dirname, 'yaml/degoogle.yml')).toString());
|
2020-05-22 04:29:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Generates a major section or "category" such as Mobile Apps
|
|
|
|
* @param {String} header Title for section
|
|
|
|
* @param {Object} data Object of data to populate README.md with
|
|
|
|
*/
|
|
|
|
function generateCategorySection(header, data) {
|
|
|
|
if (!data) return '';
|
2021-08-09 21:13:30 +02:00
|
|
|
return `## ${header}${EOL}${BACK_TO_TOP}${EOL}${EOL}`.concat(Object.values(data).map((value) => `${generateServiceSection(value)}${EOL}${EOL}`).join(''))
|
2020-05-22 04:29:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Generates a service (such as Gmail) section to be placed under a category section
|
2020-05-24 02:33:07 +02:00
|
|
|
* @param {Array} data
|
2020-05-22 04:29:03 +02:00
|
|
|
*/
|
|
|
|
function generateServiceSection(data) {
|
2020-09-01 20:53:35 +02:00
|
|
|
// Start the section with an <h4> header
|
2021-08-09 21:13:30 +02:00
|
|
|
let serviceSection = `#### ${data[0].title + EOL + EOL}`;
|
2021-07-06 05:08:43 +02:00
|
|
|
|
|
|
|
// Prep section notes
|
2021-08-09 21:13:30 +02:00
|
|
|
let notes = EOL + '';
|
2021-07-06 05:08:43 +02:00
|
|
|
|
2020-09-01 20:53:35 +02:00
|
|
|
// If there is data to be displayed, add the start of a Markdown table
|
2021-08-09 21:13:30 +02:00
|
|
|
const tableHeader = (data.filter((d) => 'name' in d).length === 0)
|
|
|
|
? `No known alternatives.${EOL}`
|
|
|
|
: `| Name | Eyes | Description |${EOL}| ---- | ---- | ----------- |${EOL}`;
|
2021-07-06 05:08:43 +02:00
|
|
|
|
|
|
|
// Add the header to the section body
|
2020-09-01 21:11:37 +02:00
|
|
|
serviceSection = serviceSection.concat(tableHeader);
|
2021-07-06 05:08:43 +02:00
|
|
|
|
2020-05-22 04:29:03 +02:00
|
|
|
// Iterate over each alternative service and add it to the table
|
2021-07-06 05:08:43 +02:00
|
|
|
data.forEach((item) => {
|
|
|
|
|
2020-05-25 16:00:51 +02:00
|
|
|
// If the object has length one, it's either title or note
|
|
|
|
if (Object.keys(item).length == 1) {
|
|
|
|
if (!item.notes) return;
|
2021-08-09 21:13:30 +02:00
|
|
|
else item.notes.forEach((note) => notes = notes.concat(`- *${note.trim()}*${EOL}`));
|
2020-05-25 16:00:51 +02:00
|
|
|
} else {
|
2021-07-06 05:08:43 +02:00
|
|
|
|
2020-05-25 16:00:51 +02:00
|
|
|
// Build the cells for the table
|
|
|
|
let name = `[${item.name}](${item.url})`;
|
|
|
|
let eyes = item.eyes ? `**${item.eyes}-eyes**` : '';
|
|
|
|
let text = item.text.trim();
|
2020-08-24 17:16:53 +02:00
|
|
|
|
2020-05-25 20:58:30 +02:00
|
|
|
// Append the F-Droid badge to the name
|
2021-03-09 22:21:11 +01:00
|
|
|
if (item.fdroid) name = name.concat('<br/>', fdroidLink(item.fdroid));
|
|
|
|
|
|
|
|
// Append the Repo badge to the name
|
|
|
|
if (item.repo) name = name.concat('<br/>', repoLink(item.repo));
|
2020-08-24 17:16:53 +02:00
|
|
|
|
2020-05-25 16:00:51 +02:00
|
|
|
// Build the row
|
2021-08-09 21:13:30 +02:00
|
|
|
const tableItem = `| ${name} | ${eyes} | ${text} |${EOL}`;
|
2020-08-24 17:16:53 +02:00
|
|
|
|
2020-05-25 16:00:51 +02:00
|
|
|
// Add the row to the table
|
2021-07-06 05:08:43 +02:00
|
|
|
serviceSection = serviceSection.concat(tableItem);
|
2020-05-25 16:00:51 +02:00
|
|
|
}
|
2020-05-21 23:48:06 +02:00
|
|
|
});
|
2021-07-06 05:08:43 +02:00
|
|
|
return `${serviceSection}${notes}`;
|
2020-05-21 23:48:06 +02:00
|
|
|
}
|
|
|
|
|
2020-05-25 21:27:14 +02:00
|
|
|
/**
|
|
|
|
* Returns a badge acting as a link to an F-Droid page for an app.
|
|
|
|
* @param {String} appId The package identifier on F-Droid
|
|
|
|
*/
|
2020-05-25 20:58:30 +02:00
|
|
|
function fdroidLink(appId) {
|
2021-03-09 22:21:11 +01:00
|
|
|
return `[![F-Droid](https://img.shields.io/f-droid/v/${appId}?style=flat-square&logo=f-droid)](https://f-droid.org/en/packages/${appId}/)`;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a badge acting as a link to a source repository for an app.
|
|
|
|
* @param {String} repo The repository url
|
|
|
|
*/
|
|
|
|
function repoLink(repo) {
|
2021-08-09 21:13:30 +02:00
|
|
|
const repoURL = new URL(repo);
|
2021-07-06 05:08:43 +02:00
|
|
|
let repoHost = path.basename(repoURL.hostname, path.extname(repoURL.hostname));
|
|
|
|
if (repoHost.includes(".")) repoHost = path.extname(repoHost).replace(".", "");
|
|
|
|
return `[![Repo](https://img.shields.io/badge/open-source-3DA639?style=flat-square&logo=${repoHost})](${repo})`;
|
2020-05-25 20:58:30 +02:00
|
|
|
}
|
|
|
|
|
2020-06-01 20:31:52 +02:00
|
|
|
/**
|
|
|
|
* Returns a badge displaying user for a Firefox addon/extension
|
|
|
|
* @param {String} link URL to extension WITHOUT trailing slash
|
|
|
|
*/
|
2020-06-01 18:42:12 +02:00
|
|
|
function addonLink(link) {
|
2021-08-09 21:13:30 +02:00
|
|
|
return (link.includes('addons.mozilla.org')) ? `![Mozilla Add-on](https://img.shields.io/amo/users/${link.split('/').pop()}?style=flat-square)` : '';
|
2020-06-01 18:42:12 +02:00
|
|
|
}
|
|
|
|
|
2020-06-01 20:31:52 +02:00
|
|
|
/**
|
|
|
|
* Returns a badge with the date of publication
|
|
|
|
* @param {String|Number} date Date of publication
|
|
|
|
*/
|
|
|
|
function dateBadge(date) {
|
2021-07-06 05:08:43 +02:00
|
|
|
return `![Published](https://img.shields.io/badge/${date.toString().replace(/\-/g, '--')}-informational?style=flat-square)`;
|
2020-06-01 20:31:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Generates a table with browser extensions and their descriptions
|
|
|
|
*/
|
2020-06-01 18:42:12 +02:00
|
|
|
function generateBrowserExtensions() {
|
2021-08-09 21:13:30 +02:00
|
|
|
return `# Browser extensions${EOL + EOL}| Name | Description |${EOL}| ---- | ----------- |${EOL}`
|
|
|
|
.concat(YAML.parse(fs.readFileSync(path.join(__dirname, 'yaml/browserExtensions.yml')).toString())
|
|
|
|
.map(({ name, text, url }) => `| [${name}](${url}) ${addonLink(url)} | ${text.trim()} |${EOL}`).join(''));
|
2020-06-01 18:42:12 +02:00
|
|
|
}
|
|
|
|
|
2020-06-01 20:31:52 +02:00
|
|
|
/**
|
|
|
|
* Generates sections for Books, Blogs, and News
|
2021-08-09 21:13:30 +02:00
|
|
|
* @param {String} pubTitle
|
2020-06-01 20:31:52 +02:00
|
|
|
* @param {String} filename
|
|
|
|
*/
|
2021-08-09 21:13:30 +02:00
|
|
|
function generatePublications(pubTitle, filename) {
|
|
|
|
return `## ${pubTitle} ${EOL + BACK_TO_TOP + EOL + EOL}| Title | Published | Author |${EOL}| ----- | --------- | ------ |${EOL}`
|
|
|
|
.concat(YAML.parse(fs.readFileSync(path.join(__dirname, `yaml/${filename}.yml`)).toString())
|
|
|
|
.map(({ title, url, date, author }) => `| [${title}](${url}) | ${dateBadge(date)} | ${author.trim()} |${EOL}`).join(''));
|
2020-06-01 20:31:52 +02:00
|
|
|
}
|
|
|
|
|
2021-08-09 21:13:30 +02:00
|
|
|
// ! Generate README.md
|
|
|
|
fs.writeFileSync(path.join(__dirname, 'README.md'), Object.values(BUILD_SECTION).map((section) => section()).join(`${EOL}${EOL}`));
|
|
|
|
console.log('Done!');
|