2020-05-21 23:48:06 +02:00
|
|
|
// Build tool for generating README.md
|
|
|
|
|
|
|
|
const os = require('os');
|
|
|
|
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
|
|
|
/**
|
|
|
|
* Main method
|
|
|
|
*/
|
2020-05-21 23:48:06 +02:00
|
|
|
function __main__() {
|
2020-05-22 04:29:03 +02:00
|
|
|
// dgSectionData will be join at the end and represents the full contents of README.md
|
2021-07-06 05:08:43 +02:00
|
|
|
const dgSectionData = [];
|
2020-05-22 04:29:03 +02:00
|
|
|
|
|
|
|
// Add all the sections
|
|
|
|
dgSectionData.push(BUILD_SECTION.header());
|
|
|
|
dgSectionData.push(BUILD_SECTION.index());
|
|
|
|
dgSectionData.push(BUILD_SECTION.contributing());
|
|
|
|
dgSectionData.push(BUILD_SECTION.browserExtensions());
|
|
|
|
dgSectionData.push(BUILD_SECTION.disclaimer());
|
|
|
|
dgSectionData.push(BUILD_SECTION.webBasedProducts());
|
|
|
|
dgSectionData.push(BUILD_SECTION.operatingSystems());
|
|
|
|
dgSectionData.push(BUILD_SECTION.desktopApps());
|
|
|
|
dgSectionData.push(BUILD_SECTION.mobileApps());
|
|
|
|
dgSectionData.push(BUILD_SECTION.hardware());
|
|
|
|
dgSectionData.push(BUILD_SECTION.useful());
|
|
|
|
dgSectionData.push(BUILD_SECTION.resources());
|
|
|
|
dgSectionData.push(BUILD_SECTION.books());
|
|
|
|
dgSectionData.push(BUILD_SECTION.blogs());
|
|
|
|
dgSectionData.push(BUILD_SECTION.news());
|
|
|
|
dgSectionData.push(BUILD_SECTION.lighterSide());
|
|
|
|
dgSectionData.push(BUILD_SECTION.closingRemarks());
|
|
|
|
|
|
|
|
// Write to the README file
|
2021-07-06 05:08:43 +02:00
|
|
|
fs.writeFileSync(path.join(__dirname, 'README.md'), dgSectionData.join(`${os.EOL}${os.EOL}`));
|
2020-05-22 04:29:03 +02:00
|
|
|
|
2020-06-01 20:31:12 +02:00
|
|
|
console.log('Done!');
|
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 '';
|
|
|
|
|
|
|
|
// Set the header to HTML <h5>
|
2021-07-06 05:08:43 +02:00
|
|
|
let categorySection = `## ${header}${os.EOL}${BACK_TO_TOP}${os.EOL}${os.EOL}`;
|
2020-05-22 04:29:03 +02:00
|
|
|
|
|
|
|
// Generate service sections for this category
|
2021-07-06 05:08:43 +02:00
|
|
|
Object.keys(data).forEach((key) => categorySection = categorySection.concat(`${generateServiceSection(data[key])}${os.EOL}${os.EOL}`));
|
2020-05-22 04:29:03 +02:00
|
|
|
|
|
|
|
return categorySection;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
let serviceSection = `#### ${data[0].title + os.EOL + os.EOL}`;
|
2021-07-06 05:08:43 +02:00
|
|
|
|
|
|
|
// Prep section notes
|
2020-05-25 16:00:51 +02:00
|
|
|
let notes = os.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
|
|
|
|
let tableHeader = `| Name | Eyes | Description |${os.EOL}| ---- | ---- | ----------- |${os.EOL}`;
|
2021-07-06 05:08:43 +02:00
|
|
|
if (data.filter((d) => 'name' in d).length === 0) tableHeader = `No known alternatives.${os.EOL}`;
|
|
|
|
|
|
|
|
// 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;
|
2020-06-01 20:31:12 +02:00
|
|
|
else item.notes.forEach((note) => notes = notes.concat(`- *${note.trim()}*${os.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-07-06 05:08:43 +02:00
|
|
|
let tableItem = `| ${name} | ${eyes} | ${text} |${os.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) {
|
|
|
|
let 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) {
|
2020-08-24 17:21:20 +02:00
|
|
|
if (!link.includes('addons.mozilla.org')) return '';
|
2021-07-06 05:08:43 +02:00
|
|
|
let addonId = link.split('/')[link.split('/').length - 1];
|
2020-06-01 23:44:10 +02:00
|
|
|
return `![Mozilla Add-on](https://img.shields.io/amo/users/${addonId}?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() {
|
|
|
|
let extensions = `# Browser extensions${os.EOL + os.EOL}| Name | Description |${os.EOL}| ---- | ----------- |${os.EOL}`;
|
|
|
|
let data = YAML.parse(fs.readFileSync(path.join(__dirname, 'yaml/browserExtensions.yml')).toString());
|
2021-07-06 05:08:43 +02:00
|
|
|
data.forEach((item) => {
|
2020-06-01 18:42:12 +02:00
|
|
|
let name = `[${item.name}](${item.url})`;
|
|
|
|
let text = item.text.trim();
|
|
|
|
let badge = addonLink(item.url);
|
2021-07-06 05:08:43 +02:00
|
|
|
let tableItem = `| ${name + ' ' + badge} | ${text} |${os.EOL}`;
|
|
|
|
extensions = extensions.concat(tableItem);
|
2020-06-01 18:42:12 +02:00
|
|
|
});
|
|
|
|
return extensions;
|
|
|
|
}
|
|
|
|
|
2020-06-01 20:31:52 +02:00
|
|
|
/**
|
|
|
|
* Generates sections for Books, Blogs, and News
|
|
|
|
* @param {String} title
|
|
|
|
* @param {String} filename
|
|
|
|
*/
|
|
|
|
function generatePublications(title, filename) {
|
|
|
|
let publications = `## ${title} ${os.EOL + BACK_TO_TOP + os.EOL + os.EOL}| Title | Published | Author |${os.EOL}| ----- | --------- | ------ |${os.EOL}`;
|
|
|
|
let data = YAML.parse(fs.readFileSync(path.join(__dirname, `yaml/${filename}.yml`)).toString());
|
2021-07-06 05:08:43 +02:00
|
|
|
data.forEach((item) => {
|
2020-06-01 20:31:52 +02:00
|
|
|
let name = `[${item.title}](${item.url})`;
|
|
|
|
let author = item.author.trim();
|
2021-07-06 05:08:43 +02:00
|
|
|
let tableItem = `| ${name} | ${dateBadge(item.date)} | ${author} |${os.EOL}`;
|
|
|
|
publications = publications.concat(tableItem);
|
2020-06-01 20:31:52 +02:00
|
|
|
});
|
|
|
|
return publications;
|
|
|
|
}
|
|
|
|
|
2021-07-06 05:08:43 +02:00
|
|
|
__main__();
|