Migrate frontend to modern stack

This (hopefully) makes changes to the frontend somewhat easier.
This commit is contained in:
Spotlight 2024-12-10 02:15:28 -06:00
parent 0f18e9f843
commit 247655fe57
No known key found for this signature in database
GPG Key ID: 874AA355B3209BDC
25 changed files with 733 additions and 2225 deletions

View File

@ -1,3 +1,4 @@
# Friends Network
This portion is run using `npm install` and `npm start`. This will compile into the `/server/templates/dist` and `/server/static` directories.
This portion is run using `npm install` and `npm run build`.
This will compile into the `/server/templates/dist` and `/server/static` directories.

File diff suppressed because it is too large Load Diff

View File

@ -1,56 +1,29 @@
{
"title": "SB Admin",
"name": "startbootstrap-sb-admin",
"name": "3ds-rpc-server",
"title": "3DS-RPC Frontend",
"description": "The web frontend for 3dsrpc.com.",
"version": "7.0.5",
"scripts": {
"build": "npm run clean && npm run build:pug && npm run build:scss && npm run build:scripts && npm run build:assets",
"build:assets": "node scripts/build-assets.js",
"build:pug": "node scripts/build-pug.js",
"build:scripts": "node scripts/build-scripts.js",
"build:scss": "node scripts/build-scss.js",
"clean": "node scripts/clean.js",
"start": "npm run build && node scripts/start.js",
"start:debug": "npm run build && node scripts/start-debug.js"
"type": "module",
"engines": {
"node": ">=20.0.0"
},
"description": "A free admin dashboard template based on Bootstrap 4, created by Start Bootstrap.",
"keywords": [
"css",
"sass",
"html",
"responsive",
"theme",
"template",
"admin",
"app",
"dashboard"
],
"homepage": "https://startbootstrap.com/template/sb-admin",
"bugs": {
"url": "https://github.com/StartBootstrap/startbootstrap-sb-admin/issues",
"email": "feedback@startbootstrap.com"
"scripts": {
"build": "node scripts/build.js",
"clean": "node scripts/clean.js"
},
"license": "MIT",
"author": "MCMi460",
"contributors": [
"David Miller (https://davidmiller.io/)"
],
"repository": {
"type": "git",
"url": "https://github.com/StartBootstrap/startbootstrap-sb-admin.git"
},
"dependencies": {
"bootstrap": "5.3.3"
},
"devDependencies": {
"autoprefixer": "10.4.20",
"browser-sync": "^3.0.2",
"chokidar": "3.6.0",
"concurrently": "8.2.2",
"postcss": "8.4.41",
"postcss": "8.4.49",
"prettier": "3.3.3",
"pug": "3.0.3",
"sass": "1.77.8",
"shelljs": "0.8.5",
"upath": "2.0.1"
"sass": "1.82.0"
}
}

View File

@ -1,5 +0,0 @@
'use strict';
const renderAssets = require('./render-assets');
renderAssets();

View File

@ -1,19 +0,0 @@
'use strict';
const upath = require('upath');
const sh = require('shelljs');
const renderPug = require('./render-pug');
const srcPath = upath.resolve(upath.dirname(__filename), '../src');
sh.find(srcPath).forEach(_processFile);
function _processFile(filePath) {
if (
filePath.match(/\.pug$/)
&& !filePath.match(/include/)
&& !filePath.match(/mixin/)
&& !filePath.match(/\/pug\/layouts\//)
) {
renderPug(filePath);
}
}

View File

@ -1,5 +0,0 @@
'use strict';
const renderScripts = require('./render-scripts');
renderScripts();

View File

@ -1,5 +0,0 @@
'use strict';
const renderSCSS = require('./render-scss');
renderSCSS();

View File

@ -0,0 +1,105 @@
import autoprefixer from "autoprefixer";
import fs from "fs/promises";
import path from "path";
import postcss from "postcss";
import prettier from "prettier";
import pug from "pug";
import * as sass from "sass";
import url from "url";
// A few common paths used throughout our script.
// We expect ./scripts/build.js, thus our base directory `.` is `../..`.
const basePath = url.fileURLToPath(import.meta.resolve("../"));
const srcPath = path.resolve(basePath, "./src");
const staticPath = path.resolve(basePath, "../static");
////////////
// Assets //
////////////
console.log(`### INFO: Copying static assets...`);
const sourceAssets = path.resolve(srcPath, "./assets");
const staticAssets = path.resolve(staticPath, "./assets");
await fs.cp(sourceAssets, staticAssets, { recursive: true });
console.log(`### INFO: Copying static JavaScript...`);
const sourceScripts = path.resolve(srcPath, "./js");
const staticScripts = path.resolve(staticPath, "./js");
await fs.cp(sourceScripts, staticScripts, { recursive: true });
//////////
// SCSS //
//////////
console.log(`### INFO: Rendering SCSS...`);
const sourceStylePath = path.resolve(srcPath, "./scss/styles.scss");
const compiledStyle = sass.compile(sourceStylePath, {
// We enable Node.js package resolution for usage with Bootstrap.
importers: [new sass.NodePackageImporter()],
// TODO: Bootstrap does not currently utilize the Sass module system.
// We sadly must silence the deprecation for @import,
// alongside Bootstrap's own usage of @import (`quietDeps`).
//
// For more information regarding the @import deprecation:
// https://sass-lang.com/documentation/breaking-changes/import/
silenceDeprecations: ["import"],
quietDeps: true,
});
const processedStyle = await postcss()
.use(autoprefixer)
.process(compiledStyle.css, { from: "styles.css", to: "styles.css" });
// Log processing warnings if necessary.
processedStyle.warnings().forEach((warn) => {
console.warn(warn.toString());
});
// Write and saved our processed CSS.
const staticCssDir = path.resolve(staticPath, "./css");
await fs.mkdir(staticCssDir, { recursive: true });
const staticStylePath = path.resolve(staticCssDir, "./styles.css");
await fs.writeFile(staticStylePath, processedStyle.css, "utf8");
////////////////////
// HTML Templates //
////////////////////
const sourceTemplateDir = path.resolve(srcPath, "./pug/pages");
const sourceTemplates = await fs.readdir(sourceTemplateDir);
// Ensure our output directory exists.
const destTemplateDir = path.resolve(basePath, "./dist");
await fs.mkdir(destTemplateDir, { recursive: true });
for (const sourceTemplatePath of sourceTemplates) {
// Ensure we only handle *.pug files.
const templateName = path.basename(sourceTemplatePath);
if (!templateName.endsWith(".pug")) {
continue;
}
// We output from src/pug/pages/*.pug to dist/*.html.
const outputName = templateName.replace(".pug", ".html");
const sourcePath = path.resolve(sourceTemplateDir, templateName);
const destPath = path.resolve(destTemplateDir, outputName);
console.log(`### INFO: Rendering ${sourcePath} to ${destPath}...`);
// First, render via Pug.
const renderedHtml = pug.renderFile(sourcePath, {
doctype: "html",
filename: templateName,
basedir: sourceTemplateDir,
});
// Next, beautify its source.
const prettifiedHtml = await prettier.format(renderedHtml, {
printWidth: 1000,
tabWidth: 4,
singleQuote: true,
proseWrap: "preserve",
endOfLine: "lf",
parser: "html",
htmlWhitespaceSensitivity: "ignore",
});
// We're done!
await fs.writeFile(destPath, prettifiedHtml, "utf-8");
}

View File

@ -1,7 +1,12 @@
const sh = require('shelljs');
const upath = require('upath');
import fs from "fs/promises";
const destPath = upath.resolve(upath.dirname(__filename), '../dist');
const destPath = new URL("../dist", import.meta.url);
sh.rm('-rf', `${destPath}/*`)
// Only delete if the directory exists.
let destExists = fs.access(destPath, fs.constants.F_OK)
.then(() => true)
.catch(() => false);
if (destExists == true) {
await fs.rm(destPath, { recursive: true });
}

View File

@ -1,11 +0,0 @@
'use strict';
const fs = require('fs');
const upath = require('upath');
const sh = require('shelljs');
module.exports = function renderAssets() {
const sourcePath = upath.resolve(upath.dirname(__filename), '../src/assets');
const destPath = upath.resolve(upath.dirname(__filename), '../../static/.');
sh.cp('-R', sourcePath, destPath)
};

View File

@ -1,35 +0,0 @@
'use strict';
const fs = require('fs');
const upath = require('upath');
const pug = require('pug');
const sh = require('shelljs');
const prettier = require('prettier');
module.exports = async function renderPug(filePath) {
const destPath = filePath.replace(/src\/pug\/\pages/, 'dist').replace(/\.pug$/, '.html');
const srcPath = upath.resolve(upath.dirname(__filename), '../src');
console.log(`### INFO: Rendering ${filePath} to ${destPath}`);
const html = pug.renderFile(filePath, {
doctype: 'html',
filename: filePath,
basedir: srcPath
});
const destPathDirname = upath.dirname(destPath);
if (!sh.test('-e', destPathDirname)) {
sh.mkdir('-p', destPathDirname);
}
const prettified = await prettier.format(html, {
printWidth: 1000,
tabWidth: 4,
singleQuote: true,
proseWrap: 'preserve',
endOfLine: 'lf',
parser: 'html',
htmlWhitespaceSensitivity: 'ignore'
});
fs.writeFileSync(destPath, prettified);
};

View File

@ -1,26 +0,0 @@
'use strict';
const fs = require('fs');
const packageJSON = require('../package.json');
const upath = require('upath');
const sh = require('shelljs');
module.exports = function renderScripts() {
const sourcePath = upath.resolve(upath.dirname(__filename), '../src/js');
const destPath = upath.resolve(upath.dirname(__filename), '../../static/.');
sh.cp('-R', sourcePath, destPath)
const sourcePathScriptsJS = upath.resolve(upath.dirname(__filename), '../src/js/scripts.js');
const destPathScriptsJS = upath.resolve(upath.dirname(__filename), '../../static/js/scripts.js');
const copyright = `/*!
* Start Bootstrap - ${packageJSON.title} v${packageJSON.version} (${packageJSON.homepage})
* Copyright 2013-${new Date().getFullYear()} ${packageJSON.author}
* Licensed under ${packageJSON.license} (https://github.com/StartBootstrap/${packageJSON.name}/blob/master/LICENSE)
*/
`
const scriptsJS = fs.readFileSync(sourcePathScriptsJS);
fs.writeFileSync(destPathScriptsJS, copyright + scriptsJS);
};

View File

@ -1,42 +0,0 @@
'use strict';
const autoprefixer = require('autoprefixer')
const fs = require('fs');
const packageJSON = require('../package.json');
const upath = require('upath');
const postcss = require('postcss')
const sass = require('sass');
const sh = require('shelljs');
const stylesPath = '../src/scss/styles.scss';
const destPath = upath.resolve(upath.dirname(__filename), '../../static/css/styles.css');
module.exports = function renderSCSS() {
const results = sass.renderSync({
data: entryPoint,
includePaths: [
upath.resolve(upath.dirname(__filename), '../node_modules')
],
});
const destPathDirname = upath.dirname(destPath);
if (!sh.test('-e', destPathDirname)) {
sh.mkdir('-p', destPathDirname);
}
postcss([ autoprefixer ]).process(results.css, {from: 'styles.css', to: 'styles.css'}).then(result => {
result.warnings().forEach(warn => {
console.warn(warn.toString())
})
fs.writeFileSync(destPath, result.css.toString());
})
};
const entryPoint = `/*!
* Start Bootstrap - ${packageJSON.title} v${packageJSON.version} (${packageJSON.homepage})
* Copyright 2013-${new Date().getFullYear()} ${packageJSON.author}
* Licensed under ${packageJSON.license} (https://github.com/StartBootstrap/${packageJSON.name}/blob/master/LICENSE)
*/
@import "${stylesPath}"
`

View File

@ -1,86 +0,0 @@
'use strict';
const _ = require('lodash');
const chokidar = require('chokidar');
const upath = require('upath');
const renderAssets = require('./render-assets');
const renderPug = require('./render-pug');
const renderScripts = require('./render-scripts');
const renderSCSS = require('./render-scss');
const watcher = chokidar.watch('src', {
persistent: true,
});
let READY = false;
process.title = 'pug-watch';
process.stdout.write('Loading');
let allPugFiles = {};
watcher.on('add', filePath => _processFile(upath.normalize(filePath), 'add'));
watcher.on('change', filePath => _processFile(upath.normalize(filePath), 'change'));
watcher.on('ready', () => {
READY = true;
console.log(' READY TO ROLL!');
});
_handleSCSS();
function _processFile(filePath, watchEvent) {
if (!READY) {
if (filePath.match(/\.pug$/)) {
if (!filePath.match(/includes/) && !filePath.match(/mixins/) && !filePath.match(/\/pug\/layouts\//)) {
allPugFiles[filePath] = true;
}
}
process.stdout.write('.');
return;
}
console.log(`### INFO: File event: ${watchEvent}: ${filePath}`);
if (filePath.match(/\.pug$/)) {
return _handlePug(filePath, watchEvent);
}
if (filePath.match(/\.scss$/)) {
if (watchEvent === 'change') {
return _handleSCSS(filePath, watchEvent);
}
return;
}
if (filePath.match(/src\/js\//)) {
return renderScripts();
}
if (filePath.match(/src\/assets\//)) {
return renderAssets();
}
}
function _handlePug(filePath, watchEvent) {
if (watchEvent === 'change') {
if (filePath.match(/includes/) || filePath.match(/mixins/) || filePath.match(/\/pug\/layouts\//)) {
return _renderAllPug();
}
return renderPug(filePath);
}
if (!filePath.match(/includes/) && !filePath.match(/mixins/) && !filePath.match(/\/pug\/layouts\//)) {
return renderPug(filePath);
}
}
function _renderAllPug() {
console.log('### INFO: Rendering All');
_.each(allPugFiles, (value, filePath) => {
renderPug(filePath);
});
}
function _handleSCSS() {
renderSCSS();
}

View File

@ -1,24 +0,0 @@
const concurrently = require('concurrently');
const upath = require('upath');
const browserSyncPath = upath.resolve(upath.dirname(__filename), '../node_modules/.bin/browser-sync');
concurrently([
{ command: 'node --inspect scripts/sb-watch.js', name: 'SB_WATCH', prefixColor: 'bgBlue.bold' },
{
command: `${browserSyncPath} dist -w --no-online`,
name: 'SB_BROWSER_SYNC',
prefixColor: 'bgBlue.bold',
}
], {
prefix: 'name',
killOthers: ['failure', 'success'],
}).then(success, failure);
function success() {
console.log('Success');
}
function failure() {
console.log('Failure');
}

View File

@ -1,24 +0,0 @@
const concurrently = require('concurrently');
const upath = require('upath');
const browserSyncPath = upath.resolve(upath.dirname(__filename), '../node_modules/.bin/browser-sync');
concurrently([
{ command: 'node scripts/sb-watch.js', name: 'SB_WATCH', prefixColor: 'bgBlue.bold' },
{
command: `"${browserSyncPath}" --reload-delay 2000 --reload-debounce 2000 dist -w --no-online`,
name: 'SB_BROWSER_SYNC',
prefixColor: 'bgGreen.bold',
}
], {
prefix: 'name',
killOthers: ['failure', 'success'],
}).then(success, failure);
function success() {
console.log('Success');
}
function failure() {
console.log('Failure');
}

View File

@ -1,13 +1,39 @@
//
// Variables
// Custom variables
//
// Import Bootstrap variables for use within theme
@import "bootstrap/scss/functions.scss";
@import "bootstrap/scss/variables.scss";
@use "sass:color";
@import "pkg:bootstrap";
// Import spacing variables
@import "./variables/spacing.scss";
// Z index variables
$zindex-content: 1037 !default;
$zindex-sidenav: 1038 !default;
$zindex-topnav: 1039 !default;
// Import navigation variables
@import "./variables/navigation.scss";
// Color variables for the sidenav
// -- Sidenav Dark
$sidenav-dark-bg: #FC8C2C;
$sidenav-dark-color: color.change($white, $alpha: 0.5);
$sidenav-dark-heading-color: #fff;
$sidenav-dark-link-color: color.change($white, $alpha: 0.8);
$sidenav-dark-link-active-color: $white;
$sidenav-dark-icon-color: color.change($white, $alpha: 0.8);
$sidenav-dark-footer-bg: #ff6d08;
// -- Sidenav Light
$sidenav-light-bg: $gray-100;
$sidenav-light-color: $gray-900;
$sidenav-light-heading-color: $gray-500;
$sidenav-light-link-color: $sidenav-light-color;
$sidenav-light-link-active-color: $primary;
$sidenav-light-icon-color: $gray-500;
$sidenav-light-footer-bg: $gray-200;
// Color variables for the topnav
$topnav-dark-toggler-color: color.change($white, $alpha: 0.5);
$topnav-light-toggler-color: $gray-900;
// Spacing variables for navigation
$topnav-base-height: 56px;
$sidenav-base-width: 225px;

View File

@ -2,6 +2,8 @@
// Default dashboard layout
//
@use "../variables" as *;
// Default behavior for the sidenav layout
// The default positioning for the sidenav is a static position
@ -26,7 +28,7 @@
justify-content: space-between;
min-width: 0;
flex-grow: 1;
min-height: calc(100vh - #{$topnav-base-height});
min-height: calc(100vh - $topnav-base-height);
margin-left: -$sidenav-base-width;
}
}
@ -47,7 +49,7 @@
left: 0;
width: 100%;
height: 100%;
background: $black;
background: black;
z-index: $zindex-content;
opacity: 0.5;
transition: opacity 0.3s ease-in-out;

View File

@ -1,6 +1,7 @@
//
// Fixed dashboard layout
//
@use "../variables" as *;
.sb-nav-fixed {
background: repeating-linear-gradient(

View File

@ -1,6 +1,7 @@
//
// Topnav
//
@use "../variables" as *;
.sb-topnav {
padding-left: 0;

View File

@ -1,6 +1,7 @@
//
// Sidenav Dark
//
@use "../../variables" as *;
// Dark theme for sidenav
// Append .sb-sidenav-dark to .sb-sidenav to use

View File

@ -1,6 +1,7 @@
//
// Sidenav Light
//
@use "../../variables" as *;
// Light theme for sidenav
// Append .sb-sidenav-light to .sb-sidenav to use

View File

@ -2,26 +2,22 @@
// Styles
//
// Import Custom Variables
@import "variables.scss";
// Import Bootstrap
@import "bootstrap/scss/bootstrap.scss";
// Import Custom SCSS
@import "global.scss";
// Plase note that we `@import` the entirety of
// Bootstrap within `_variables.scss`.
//
// It is not imported here because it gets `@use`d
// within several of our styles below.
@use "global.scss";
// Layout
@import "layout/authentication.scss";
@import "layout/dashboard-default.scss";
@import "layout/dashboard-fixed.scss";
@import "layout/error.scss";
@use "layout/authentication.scss";
@use "layout/dashboard-default.scss";
@use "layout/dashboard-fixed.scss";
@use "layout/error.scss";
// Navigation
@import "navigation/nav.scss";
@import "navigation/topnav.scss";
@import "navigation/sidenav/sidenav.scss";
@import "navigation/sidenav/sidenav-dark.scss";
@import "navigation/sidenav/sidenav-light.scss";
// Plugins
@use "navigation/nav.scss";
@use "navigation/topnav.scss";
@use "navigation/sidenav/sidenav.scss";
@use "navigation/sidenav/sidenav-dark.scss";
@use "navigation/sidenav/sidenav-light.scss";

View File

@ -1,32 +0,0 @@
//
// Navigation
//
// Z index variables
$zindex-content: 1037 !default;
$zindex-sidenav: 1038 !default;
$zindex-topnav: 1039 !default;
// Color variables for the sidenav
// -- Sidenav Dark
$sidenav-dark-bg: #FC8C2C;
$sidenav-dark-color: fade-out($white, 0.5);
$sidenav-dark-heading-color: #fff;
$sidenav-dark-link-color: fade-out($white, 0.2);
$sidenav-dark-link-active-color: $white;
$sidenav-dark-icon-color: fade-out($white, 0.2);
$sidenav-dark-footer-bg: #ff6d08;
// -- Sidenav Light
$sidenav-light-bg: $gray-100;
$sidenav-light-color: $gray-900;
$sidenav-light-heading-color: $gray-500;
$sidenav-light-link-color: $sidenav-light-color;
$sidenav-light-link-active-color: $primary;
$sidenav-light-icon-color: $gray-500;
$sidenav-light-footer-bg: $gray-200;
// Color variables for the topnav
$topnav-dark-toggler-color: fade-out($white, 0.5);
$topnav-light-toggler-color: $gray-900;

View File

@ -1,7 +0,0 @@
//
// Spacing
//
// Spacing variables for navigation
$topnav-base-height: 56px;
$sidenav-base-width: 225px;