From 462e5b5bfffede8f589d96743ba0ba7fe48c99ac Mon Sep 17 00:00:00 2001 From: Xaymar Date: Tue, 24 Feb 2026 00:14:31 +0100 Subject: [PATCH] Initial frontend code --- .gitignore | 3 +- backend/framework/http/response.php | 1 + frontend/.editorconfig | 11 ++++ frontend/eleventy.config.js | 86 +++++++++++++++++++++++++++++ frontend/package-lock.json | 41 ++++++++++++++ frontend/package.json | 16 ++++++ 6 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 frontend/.editorconfig create mode 100644 frontend/eleventy.config.js create mode 100644 frontend/package-lock.json create mode 100644 frontend/package.json diff --git a/.gitignore b/.gitignore index 98e2516..c78d3b6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /venv -/build \ No newline at end of file +/build +/frontend/node_modules diff --git a/backend/framework/http/response.php b/backend/framework/http/response.php index 241efce..c1a701d 100644 --- a/backend/framework/http/response.php +++ b/backend/framework/http/response.php @@ -37,6 +37,7 @@ class Response { // Ensure we cleanly stop everything. $this->stop(); + // Stop and clean the output buffer. ob_end_clean(); } diff --git a/frontend/.editorconfig b/frontend/.editorconfig new file mode 100644 index 0000000..3ef06e4 --- /dev/null +++ b/frontend/.editorconfig @@ -0,0 +1,11 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 +indent_style = tab +indent_size = 4 + +[*.{htm,html,liquid}] +insert_final_newline = false diff --git a/frontend/eleventy.config.js b/frontend/eleventy.config.js new file mode 100644 index 0000000..3cae49e --- /dev/null +++ b/frontend/eleventy.config.js @@ -0,0 +1,86 @@ +import path from "node:path"; + +import { HtmlBasePlugin as plugin_html } from "@11ty/eleventy"; +import { default as plugin_navigation } from "@11ty/eleventy-navigation"; +import { default as plugin_ejs } from "@11ty/eleventy-plugin-ejs"; +import { default as plugin_less } from "./plugins/less.js"; +import { default as plugin_minify_html } from "./plugins/minify-html.js"; +import { default as plugin_minify_js } from "./plugins/minify-js.js"; +import { default as plugin_minify_css } from "./plugins/minify-css.js"; + +export default async function (eleventyConfig) { + // Watch Targets + eleventyConfig.addWatchTarget(path.format(path.parse("./data/**/*"))); + eleventyConfig.addWatchTarget(path.format(path.parse("./styles/**/*"))); + eleventyConfig.addWatchTarget(path.format(path.parse("./assets/**/*"))); + + // Passthroughs + eleventyConfig.addPassthroughCopy({ + [`${path.format(path.parse("./assets"))}`]: path.format(path.parse("/assets")), + }); + + // Plugins + eleventyConfig.addPlugin(plugin_html); + eleventyConfig.addPlugin(plugin_navigation); + eleventyConfig.addPlugin(plugin_ejs); + eleventyConfig.addPlugin(plugin_less); + eleventyConfig.addPlugin(plugin_minify_html, { minifyOptions: { + caseSensitive: true, + collapseBooleanAttributes: true, + collapseInlineTagWhitespace: true, + collapseWhitespace: true, + conservativeCollapse: true, + decodeEntities: true, + html5: true, + keepClosingSlash: true, + minifyCSS: true, + minifyJS: true, + minifyURLs: true, + preserveLineBreaks: false, + quoteCharacter: "\"", + removeComments: true, + removeScriptTypeAttributes: true, + removeStyleLinkTypeAttributes: true, + sortAttributes: true, + sortClassName: false, // Breaks CSS selector order! + useShortDoctype: true }}); + eleventyConfig.addPlugin(plugin_minify_js, { minifyOptions: { + mangle: false, + sourceMap: { url: "inline" }} + }); + eleventyConfig.addPlugin(plugin_minify_css, { minifyOptions: { + level: 2, + inline: false, + sourceMap: true, + sourceMapInlineSources: true, + }}); + + // Liquid configuration + eleventyConfig.setLiquidParameterParsing("builtin"); + + // Old Page Redirection + eleventyConfig.addCollection("redirects", function(pageApi) { + let redirects = []; + + for(let page of pageApi.getAll()) { + let aliases = page.data?.aliases; + for (let idx in aliases) { + console.log(`Aliasing '${aliases[idx]}' to '${page.data.page.url}'`); + redirects.push([`/${aliases[idx]}`, page]); + } + } + + return redirects; + }) +}; + +export const config = { + "dir": { + "output": "build", + "input": "source", + "data": path.format(path.parse("../data")), + "layouts": path.format(path.parse("../layouts")), + "includes": path.format(path.parse("../includes")), + "styles": path.format(path.parse("../styles")), + }, +} diff --git a/frontend/package-lock.json b/frontend/package-lock.json new file mode 100644 index 0000000..ce4735c --- /dev/null +++ b/frontend/package-lock.json @@ -0,0 +1,41 @@ +{ + "name": "frontend", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "frontend", + "version": "1.0.0", + "license": "ISC", + "devDependencies": { + "eleventy": "^2.0.1", + "typescript": "^5.9.3" + } + }, + "node_modules/eleventy": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/eleventy/-/eleventy-2.0.1.tgz", + "integrity": "sha512-Zsh2rCvXxxjIDj9s93YhhfqmIzhUpv4VQgS7QxFB51btWO/09xdm7/Da/KmDpunTQ8wWsvW2Jo8a6gDcCc+6VA==", + "dev": true, + "license": "MIT", + "bin": { + "eleventy": "cmd.js" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + } + } +} diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000..954fcc6 --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,16 @@ +{ + "name": "frontend", + "version": "1.0.0", + "description": "", + "license": "ISC", + "author": "", + "type": "commonjs", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "devDependencies": { + "eleventy": "^2.0.1", + "typescript": "^5.9.3" + } +}