🥂 Initial commit

This commit is contained in:
Lucas Colombo
2023-07-01 16:19:40 -03:00
commit 65a7943588
488 changed files with 57991 additions and 0 deletions

34
tools/tasks/copy-to.js Normal file
View File

@ -0,0 +1,34 @@
import fs from 'fs';
import path from 'path';
import { Logger } from '../utils/logger.js';
const logger = new Logger(copyTo.name, 'info', 'brightYellow');
export async function copyTo(sourcePath, targetPath) {
logger.info(`Copying ${sourcePath} to ${targetPath}`);
await recursiveCopy(sourcePath, targetPath);
logger.info(`Copy has finished!`);
}
async function recursiveCopy(sourcePath, targetPath) {
// Create the target directory if it doesn't exist
if (!fs.existsSync(targetPath)) {
fs.mkdirSync(targetPath, { recursive: true });
}
// Get all files and directories in the source path
const files = fs.readdirSync(sourcePath, { withFileTypes: true });
for (const file of files) {
const sourceFile = path.join(sourcePath, file.name);
const targetFile = path.join(targetPath, file.name);
if (file.isDirectory()) {
// Recursively copy directories
await recursiveCopy(sourceFile, targetFile);
} else {
// Copy files
fs.copyFileSync(sourceFile, targetFile);
}
}
}

33
tools/tasks/deploy.js Normal file
View File

@ -0,0 +1,33 @@
import { Logger } from '../utils/logger.js';
import { buildScss } from './scss.js';
import { buildFonts } from './fonts.js';
import { buildTemplates } from './templates.js';
import { copyTo } from './copy-to.js';
import { restartService } from './restart-service.js';
import { extname } from 'path';
const logger = new Logger('deploy', 'info', 'brightMagenta');
export async function deploy(srcPath, distPath, serverPath, serviceName, file = null) {
logger.info('Deploying...');
let shouldRestart = true;
// check if it's an scss
if (file !== null && file !== undefined && extname(file) === '.scss') {
shouldRestart = false;
}
try {
await buildScss(srcPath, distPath);
await buildFonts(srcPath, distPath);
await buildTemplates(srcPath, distPath);
await copyTo(distPath, serverPath);
shouldRestart && await restartService(serviceName);
logger.info('Deployment successful!');
} catch (error) {
logger.error(`Deployment failed: ${error}`);
}
}

32
tools/tasks/fonts.js Normal file
View File

@ -0,0 +1,32 @@
import { copyFileSync, mkdirSync } from 'fs';
import { join } from 'path';
import { readFiles } from '../utils/funcs.js';
import { Logger } from '../utils/logger.js';
const logger = new Logger(buildFonts.name, 'info', 'brightCyan');
const imgSrc = 'themes/fonts';
const imgDest = '/public/fonts';
export async function buildFonts(srcHome, distHome) {
logger.info('Fonts build has started');
const fontsSrcPath = join(srcHome, imgSrc);
const fontsDestPath = join(distHome, imgDest);
mkdirSync(fontsDestPath, { recursive: true });
const files = readFiles(fontsSrcPath, [
'.woff',
'.woff2',
'.ttf',
'.eot',
'.svg',
'.otf',
]);
for (const file of files) {
// just copy the file
copyFileSync(join(fontsSrcPath, file), join(fontsDestPath, file));
}
logger.info('Fonts build has finished');
}

129
tools/tasks/img.js Normal file
View File

@ -0,0 +1,129 @@
import { fabric } from 'fabric';
import imageminZopfli from 'imagemin-zopfli';
import { readFile, writeFile } from 'node:fs/promises';
import { join, basename } from 'path';
import { optimize } from 'svgo';
import { readFiles } from '../utils/funcs.js';
import { Logger } from '../utils/logger.js';
import { mkdirSync, copyFileSync } from 'fs';
const logger = new Logger(buildImg.name, 'info', 'brightGreen');
const imgSrc = 'themes/img';
const imgDest = '/public/img';
export async function buildImg(srcHome, distHome) {
logger.info('Images build has started');
const imgSrcPath = join(srcHome, imgSrc);
const imgDestPath = join(distHome, imgDest);
const images = { logos: { logo: undefined, favicon: undefined }, others: [] };
mkdirSync(imgDestPath, { recursive: true });
const files = readFiles(imgSrcPath, ['.svg', '.png', '.jpg', '.webp', '.gif']);
// Separate logo.svg and favicon.svg from the rest
files.forEach((file) => {
if (file === 'logo.svg') {
images.logos.logo = join(imgSrcPath, file);
} else if (file === 'favicon.svg') {
images.logos.favicon = join(imgSrcPath, file);
} else {
images.others.push(join(imgSrcPath, file));
}
});
await Promise.all([
processLogos(images.logos, imgDestPath),
processOthers(images.others, imgDestPath),
])
logger.info('Images build has finished');
}
async function processLogos(logos, distHome) {
const promises = [];
if (logos.logo) {
const svg = await readFile(logos.logo, 'utf8');
promises.push(generate(svg, join(distHome, 'logo.svg'), { size: 32 }));
promises.push(generate(svg, join(distHome, 'logo.png'), { size: 512 }));
}
if (logos.favicon) {
const svg = await readFile(logos.favicon, 'utf8');
promises.push(
generate(svg, join(distHome, 'favicon.svg'), { size: 32 }),
generate(svg, join(distHome, 'favicon.png'), { size: 180 }),
generate(svg, join(distHome, 'apple-touch-icon.png'), { size: 180, bg: true }),
generate(svg, join(distHome, 'avatar_default.png'), { size: 200, bg: true })
);
}
await Promise.all(promises);
}
function loadSvg(svg) {
return new Promise((resolve) => {
fabric.loadSVGFromString(svg, (objects, options) => {
resolve({ objects, options });
});
});
}
async function generate(svg, path, { size, bg }) {
if (String(path).endsWith('.svg')) {
const { data } = optimize(svg, {
plugins: [
'preset-default',
'removeDimensions',
{
name: 'addAttributesToSVGElement',
params: { attributes: [{ height: size }] },
},
],
});
await writeFile(path, data);
return;
}
const { objects, options } = await loadSvg(svg);
const canvas = new fabric.Canvas();
const newWidth = size * options.width / options.height;
canvas.setDimensions({ width: newWidth, height: size });
const ctx = canvas.getContext('2d');
ctx.scale(
options.width ? newWidth / options.width : 1,
options.height ? size / options.height : 1
);
if (bg) {
canvas.add(
new fabric.Rect({
left: 0,
top: 0,
height: size * (1 / (size / options.height)),
width: size * (1 / (size / options.width)),
fill: 'black',
})
);
}
canvas.add(fabric.util.groupSVGElements(objects, options));
canvas.renderAll();
let png = Buffer.from([]);
for await (const chunk of canvas.createPNGStream()) {
png = Buffer.concat([png, chunk]);
}
png = await imageminZopfli({ more: true })(png);
await writeFile(path, png);
}
async function processOthers(others, distHome) {
// just copy the rest of the images to dist
for (const img of others) {
copyFileSync(img, join(distHome, basename(img)));
}
}

View File

@ -0,0 +1,31 @@
import { exec } from 'child_process';
import { Logger } from '../utils/logger.js';
const logger = new Logger(restartService.name, 'info', 'brightRed');
export async function restartService(serviceName) {
return new Promise((resolve, reject) => {
logger.info(`Restarting '${serviceName}' service...`);
let command;
let args;
if (process.platform === 'win32') {
command = 'cmd.exe';
args = ['/c', 'net', 'stop', serviceName, '&&', 'net', 'start', serviceName];
} else {
command = 'sudo';
args = ['systemctl', 'restart', serviceName];
}
exec(`${command} ${args.join(' ')}`, (error, stdout) => {
if (error) {
logger.error(`Failed to restart '${serviceName}' service: ${error}`);
reject(error);
} else {
logger.info(`'${serviceName}' service restarted!`);
resolve(stdout);
}
});
});
}

64
tools/tasks/scss.js Normal file
View File

@ -0,0 +1,64 @@
import { mkdirSync, readdirSync, writeFileSync } from 'fs';
import { join } from 'path';
import { Logger } from '../utils/logger.js';
import { compile } from 'sass';
const logger = new Logger(buildScss.name, 'debug', 'pink');
const scss_home = 'themes/scss';
const css_home = '/public/css';
export async function buildScss(src_home, dist_home) {
logger.info('SCSS build has started');
const themes = get_themes(src_home);
mkdirSync(join(dist_home, css_home), { recursive: true });
for (const theme of themes) {
logger.debug(`Building ${theme.name} theme`);
const result = compile(theme.path, {
loadPaths: [join(src_home, scss_home), join(src_home, '../node_modules')],
quietDeps: true,
logger: {
debug: logger.simpleDebug.bind(logger),
info: logger.simpleInfo.bind(logger),
warn: logger.simpleWarn.bind(logger),
error: logger.simpleError.bind(logger),
}
});
logger.debug(`Writing ${theme.name} theme to disk`);
writeFileSync(
join(dist_home, css_home, `theme-${theme.name}.css`),
result.css
);
}
logger.info('SCSS build has finished');
}
function get_themes(src_home) {
return readdirSync(join(src_home, scss_home)).filter(
(fn) => fn.endsWith('.scss') && !fn.startsWith('_')
).map((file) => ({
name: file.replace('.scss', ''),
path: join(src_home, scss_home, file),
}))
}
// for (const flavor of Object.keys(variants)) {
// for (const accent of accents) {
// const input = builder(flavor, accent);
// const result = compileString(input, {
// loadPaths: [join(__dirname, 'src'), join(__dirname, 'node_modules')],
// });
// mkdirSync(join(__dirname, 'dist'), { recursive: true });
// writeFileSync(
// join(__dirname, 'dist', `theme-catppuccin-${flavor}-${accent}.css`),
// result.css
// );
// }
// }

17
tools/tasks/templates.js Normal file
View File

@ -0,0 +1,17 @@
import { join } from 'path';
import { copyFolderRecursiveSync } from '../utils/funcs.js';
import { Logger } from '../utils/logger.js';
const logger = new Logger(buildTemplates.name, 'info', 'blue');
const imgSrc = 'templates';
const imgDest = '/';
export async function buildTemplates(srcHome, distHome) {
logger.info('Fonts build has started');
const tmplSrcPath = join(srcHome, imgSrc);
const tmplDestPath = join(distHome, imgDest);
// just copy the entire tmplSrcPath to tmplDestPath
copyFolderRecursiveSync(tmplSrcPath, tmplDestPath);
logger.info('Templates build has finished');
}