More docker updates
Minifying CSS
This commit is contained in:
parent
219841a148
commit
8b21ff6e68
4
.gitignore
vendored
4
.gitignore
vendored
@ -2,4 +2,6 @@ node_modules
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
dist
|
dist
|
||||||
src/*.js
|
src/*.js
|
||||||
src/**/*.js
|
src/**/*.js
|
||||||
|
public/index.html
|
||||||
|
public/main.*.css
|
18
default.conf
Normal file
18
default.conf
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
server {
|
||||||
|
listen 4173;
|
||||||
|
|
||||||
|
root /app;
|
||||||
|
|
||||||
|
location /app {
|
||||||
|
try_files $uri $uri/ =404;
|
||||||
|
|
||||||
|
# Cache settings for static content
|
||||||
|
expires 7d; # Cache static content for 7 days
|
||||||
|
add_header Cache-Control "public, max-age=604800, immutable";
|
||||||
|
}
|
||||||
|
|
||||||
|
# Additional configurations can be added here if needed
|
||||||
|
# For example, error handling, logging, etc.
|
||||||
|
error_page 404 /index.html;
|
||||||
|
error_page 500 502 503 504 /index.html;
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
# Build site using Node JS
|
# Build site using Node JS
|
||||||
FROM node:21-slim
|
FROM node:21-slim
|
||||||
|
|
||||||
|
# Install nginx
|
||||||
RUN apt-get update && apt-get install -y nginx
|
RUN apt-get update && apt-get install -y nginx
|
||||||
|
|
||||||
ARG BUILD_DATE
|
ARG BUILD_DATE
|
||||||
@ -19,6 +20,9 @@ RUN npm i
|
|||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
|
# Copy the nginx config to the correct folder
|
||||||
|
COPY default.conf /etc/nginx/conf.d/default.conf
|
||||||
|
|
||||||
ENV NODE_ENV production
|
ENV NODE_ENV production
|
||||||
|
|
||||||
ENV TITLE "My Website"
|
ENV TITLE "My Website"
|
||||||
@ -37,4 +41,4 @@ EXPOSE 4173
|
|||||||
RUN chmod +x /app/docker-entrypoint.sh
|
RUN chmod +x /app/docker-entrypoint.sh
|
||||||
ENTRYPOINT ["/app/docker-entrypoint.sh"]
|
ENTRYPOINT ["/app/docker-entrypoint.sh"]
|
||||||
|
|
||||||
CMD ["npm", "run", "start"]
|
CMD ["npm", "run", "build"]
|
@ -5,7 +5,7 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no" />
|
||||||
<title>My Website</title>
|
<title>My Website</title>
|
||||||
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" />
|
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" />
|
||||||
<link rel="stylesheet" crossorigin="" href="./main.css" />
|
<link rel="stylesheet" href="./main.css" crossorigin="" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
33
package-lock.json
generated
33
package-lock.json
generated
@ -9,10 +9,14 @@
|
|||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "^20.10.5",
|
"@types/node": "^20.10.5",
|
||||||
|
"clean-css": "^5.3.3",
|
||||||
"html-minifier": "^4.0.0",
|
"html-minifier": "^4.0.0",
|
||||||
"prettier": "^3.1.1",
|
"prettier": "^3.1.1",
|
||||||
"tailwindcss": "^3.3.6",
|
"tailwindcss": "^3.3.6",
|
||||||
"typescript": "^5.3.3"
|
"typescript": "^5.3.3"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/clean-css": "^4.2.11"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@alloc/quick-lru": {
|
"node_modules/@alloc/quick-lru": {
|
||||||
@ -106,6 +110,16 @@
|
|||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/clean-css": {
|
||||||
|
"version": "4.2.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/clean-css/-/clean-css-4.2.11.tgz",
|
||||||
|
"integrity": "sha512-Y8n81lQVTAfP2TOdtJJEsCoYl1AnOkqDqMvXb9/7pfgZZ7r8YrEyurrAvAoAjHOGXKRybay+5CsExqIH6liccw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "*",
|
||||||
|
"source-map": "^0.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "20.10.5",
|
"version": "20.10.5",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.5.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.5.tgz",
|
||||||
@ -224,14 +238,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/clean-css": {
|
"node_modules/clean-css": {
|
||||||
"version": "4.2.1",
|
"version": "5.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz",
|
||||||
"integrity": "sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==",
|
"integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"source-map": "~0.6.0"
|
"source-map": "~0.6.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 4.0"
|
"node": ">= 10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/commander": {
|
"node_modules/commander": {
|
||||||
@ -408,6 +422,17 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/html-minifier/node_modules/clean-css": {
|
||||||
|
"version": "4.2.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.4.tgz",
|
||||||
|
"integrity": "sha512-EJUDT7nDVFDvaQgAo2G/PJvxmp1o/c6iXLbswsBbUFXi1Nr+AjA2cKmfbKDMjMvzEe75g3P6JkaDDAKk96A85A==",
|
||||||
|
"dependencies": {
|
||||||
|
"source-map": "~0.6.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/html-minifier/node_modules/commander": {
|
"node_modules/html-minifier/node_modules/commander": {
|
||||||
"version": "2.20.3",
|
"version": "2.20.3",
|
||||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||||
|
11
package.json
11
package.json
@ -8,12 +8,14 @@
|
|||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"type": "commonjs",
|
"type": "commonjs",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsc && node ./dist/static.js && npm run html && npm run tailwind",
|
"build": "tsc && node ./dist/index.js && npm run html && npm run tailwind && npm run css-cache-break",
|
||||||
"html": "html-minifier --remove-comments --collapse-whitespace --input-dir ./dist --output-dir ./dist --file-ext html",
|
"html": "html-minifier --remove-comments --collapse-whitespace --input-dir ./public --output-dir ./public --file-ext html",
|
||||||
"tailwind": "npx tailwindcss -i ./src/tailwind.css -o ./dist/main.css"
|
"tailwind": "npx tailwindcss -i ./src/tailwind.css -o ./public/main.css",
|
||||||
|
"css-cache-break": "node ./dist/css-cache-break.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "^20.10.5",
|
"@types/node": "^20.10.5",
|
||||||
|
"clean-css": "^5.3.3",
|
||||||
"html-minifier": "^4.0.0",
|
"html-minifier": "^4.0.0",
|
||||||
"prettier": "^3.1.1",
|
"prettier": "^3.1.1",
|
||||||
"tailwindcss": "^3.3.6",
|
"tailwindcss": "^3.3.6",
|
||||||
@ -27,5 +29,8 @@
|
|||||||
"tabWidth": 4,
|
"tabWidth": 4,
|
||||||
"trailingComma": "es5",
|
"trailingComma": "es5",
|
||||||
"useTabs": true
|
"useTabs": true
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/clean-css": "^4.2.11"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
23
src/css-cache-break.ts
Normal file
23
src/css-cache-break.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import CleanCSS from "clean-css";
|
||||||
|
import * as path from "path";
|
||||||
|
import { sbReadFile, sbRename, sbWriteFile } from "./shared/files";
|
||||||
|
|
||||||
|
const mainCSSFilename = `main.${new Date().getTime()}.css`;
|
||||||
|
|
||||||
|
const cssFileInPath = path.join(__dirname, "../", "./public", "main.css");
|
||||||
|
const cssFileOutPath = path.join(__dirname, "../", "./public", mainCSSFilename);
|
||||||
|
const indexFileInOutPath = path.join(__dirname, "../", "./public", "index.html");
|
||||||
|
|
||||||
|
async function start(): Promise<void> {
|
||||||
|
await sbRename(cssFileInPath, cssFileOutPath);
|
||||||
|
const css = await sbReadFile(cssFileOutPath);
|
||||||
|
await sbWriteFile(cssFileOutPath, new CleanCSS().minify(css).styles);
|
||||||
|
|
||||||
|
const index = await sbReadFile(indexFileInOutPath);
|
||||||
|
const cssLink = `<link rel="stylesheet" href="./main.css" crossorigin="">`;
|
||||||
|
const cssLinkReplacement = `<link rel="stylesheet" href="./${mainCSSFilename}" crossorigin="" />`;
|
||||||
|
|
||||||
|
await sbWriteFile(indexFileInOutPath, index.replace(cssLink, cssLinkReplacement));
|
||||||
|
}
|
||||||
|
|
||||||
|
start();
|
21
src/index.ts
Normal file
21
src/index.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import * as path from "path";
|
||||||
|
import { IndexPage } from "./pages/index";
|
||||||
|
import { sbReadFile, sbWriteFile } from "./shared/files";
|
||||||
|
import { PAGEICON, PAGETITLE } from "./variables";
|
||||||
|
|
||||||
|
const indexFileInPath = path.join(__dirname, "../", "index.html");
|
||||||
|
const indexFileOutPath = path.join(__dirname, "../", "./public", "index.html");
|
||||||
|
|
||||||
|
async function start(): Promise<void> {
|
||||||
|
const index = await sbReadFile(indexFileInPath);
|
||||||
|
|
||||||
|
const newText = IndexPage({ icon: PAGEICON, title: PAGETITLE });
|
||||||
|
const rootDiv = '<div id="root"></div>';
|
||||||
|
const rootDivReplacement = '<div id="root">$1</div>';
|
||||||
|
|
||||||
|
const newIndex = index.replace(rootDiv, rootDivReplacement.replace("$1", newText));
|
||||||
|
|
||||||
|
await sbWriteFile(indexFileOutPath, newIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
start();
|
38
src/shared/files.ts
Normal file
38
src/shared/files.ts
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import * as fs from "fs/promises";
|
||||||
|
|
||||||
|
export async function sbReadFile(fileName: string): Promise<string> {
|
||||||
|
return new Promise(async (resolve, reject) => {
|
||||||
|
try {
|
||||||
|
const index = await fs.readFile(fileName);
|
||||||
|
|
||||||
|
return resolve(index.toString());
|
||||||
|
} catch (exception) {
|
||||||
|
console.error(`Could not read file: ${fileName}`);
|
||||||
|
reject(exception);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function sbWriteFile(fileName: string, contents: string): Promise<boolean> {
|
||||||
|
return new Promise(async (resolve, reject) => {
|
||||||
|
try {
|
||||||
|
await fs.writeFile(fileName, contents);
|
||||||
|
return resolve(true);
|
||||||
|
} catch (exception) {
|
||||||
|
console.error(`Could not write file: ${fileName}`);
|
||||||
|
reject(exception);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function sbRename(fileNameOld: string, fileNameNew: string): Promise<boolean> {
|
||||||
|
return new Promise(async (resolve, reject) => {
|
||||||
|
try {
|
||||||
|
await fs.rename(fileNameOld, fileNameNew);
|
||||||
|
return resolve(true);
|
||||||
|
} catch (exception) {
|
||||||
|
console.error(`Could not rename file: ${fileNameOld} to ${fileNameNew}`);
|
||||||
|
reject(exception);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
@ -1,46 +0,0 @@
|
|||||||
import * as fs from "fs/promises";
|
|
||||||
import * as path from "path";
|
|
||||||
import { IndexPage } from "./pages/index";
|
|
||||||
import { PAGEICON, PAGETITLE } from "./variables";
|
|
||||||
|
|
||||||
const indexFileInPath = path.join(__dirname, "../", "index.html");
|
|
||||||
const indexFileOutPath = path.join(__dirname, "../", "./dist", "index.html");
|
|
||||||
|
|
||||||
async function readIndexPage(): Promise<string> {
|
|
||||||
return new Promise(async (resolve, reject) => {
|
|
||||||
try {
|
|
||||||
const index = await fs.readFile(indexFileInPath);
|
|
||||||
|
|
||||||
return resolve(index.toString());
|
|
||||||
} catch (exception) {
|
|
||||||
console.error("Could not read index.html file");
|
|
||||||
reject(exception);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function writeIndexPage(contents: string): Promise<boolean> {
|
|
||||||
return new Promise(async (resolve, reject) => {
|
|
||||||
try {
|
|
||||||
await fs.writeFile(indexFileOutPath, contents);
|
|
||||||
return resolve(true);
|
|
||||||
} catch (exception) {
|
|
||||||
console.error("Could not write index.html file");
|
|
||||||
reject(exception);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function start(): Promise<void> {
|
|
||||||
const index = await readIndexPage();
|
|
||||||
|
|
||||||
const newText = IndexPage({ icon: PAGEICON, title: PAGETITLE });
|
|
||||||
const rootDiv = '<div id="root"></div>';
|
|
||||||
const rootDivReplacement = '<div id="root">$1</div>';
|
|
||||||
|
|
||||||
const newIndex = index.replace(rootDiv, rootDivReplacement.replace("$1", newText));
|
|
||||||
|
|
||||||
await writeIndexPage(newIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
start();
|
|
@ -8,5 +8,5 @@
|
|||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"module": "CommonJS"
|
"module": "CommonJS"
|
||||||
},
|
},
|
||||||
"include": ["src/static.ts"]
|
"include": ["src/index.ts", "src/css-cache-break.ts"]
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user