build(frontend): rework bundler (esbuild) asset and static file embedding using manifest

This commit is contained in:
Sarah Vaupel 2024-10-18 18:06:38 +02:00
parent efe53ad5a1
commit 0f990db1ea
12 changed files with 426 additions and 57 deletions

2
.gitignore vendored
View File

@ -46,7 +46,7 @@ test.log
/.stack-work.lock /.stack-work.lock
/.npmrc /.npmrc
/.npm/ /.npm/
/config/webpack.yml /config/manifest.json
tunnel.log tunnel.log
/static /static
/well-known /well-known

View File

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 Gregor Kleen <gregor.kleen@ifi.lmu.de>,Sarah Vaupel <sarah.vaupel@ifi.lmu.de>,Steffen Jost <jost@tcs.ifi.lmu.de>,Wolfgang Witt <Wolfgang.Witt@campus.lmu.de> # SPDX-FileCopyrightText: 2022-2024 Sarah Vaupel <sarah.vaupel@uniworx.de>, Gregor Kleen <gregor.kleen@ifi.lmu.de>,Sarah Vaupel <sarah.vaupel@ifi.lmu.de>,Steffen Jost <jost@tcs.ifi.lmu.de>,Wolfgang Witt <Wolfgang.Witt@campus.lmu.de>
# #
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
@ -9,9 +9,9 @@
static-dir: "_env:STATIC_DIR:static" static-dir: "_env:STATIC_DIR:static"
well-known-dir: "_env:WELL_KNOWN_DIR:well-known" well-known-dir: "_env:WELL_KNOWN_DIR:well-known"
well-known-link-file: html_code.html well-known-link-file: include.html
webpack-manifest: "_env:WEBPACK_MANIFEST:config/webpack.yml" bundler-manifest: "_env:BUNDLER_MANIFEST:config/manifest.json"
host: "_env:HOST:*4" # any IPv4 host host: "_env:HOST:*4" # any IPv4 host
port: "_env:PORT:3000" port: "_env:PORT:3000"
ip-from-header: "_env:IP_FROM_HEADER:false" ip-from-header: "_env:IP_FROM_HEADER:false"

View File

@ -1,36 +1,65 @@
import * as esbuild from 'esbuild'; import * as esbuild from 'esbuild';
import { sassPlugin } from 'esbuild-sass-plugin'; import { sassPlugin } from 'esbuild-sass-plugin';
import svgPlugin from 'esbuild-plugin-svg-bundle'; // import svgPlugin from 'esbuild-plugin-svg-bundle';
import svgPlugin from 'esbuild-plugin-svg';
import { copy } from 'esbuild-plugin-copy'; import { copy } from 'esbuild-plugin-copy';
// import manifestPlugin from 'esbuild-plugin-manifest';
import manifestPlugin from 'esbuild-plugin-assets-manifest';
import copyWithHashPlugin from '@enonic/esbuild-plugin-copy-with-hash';
import inlineImportPlugin from 'esbuild-plugin-inline-import';
const staticDir = './static';
const wellKnownDir = './well-known';
await esbuild.build({ await esbuild.build({
bundle: true,
minify: true,
sourcemap: true,
entryPoints: {
main: './frontend/src/main.js',
polyfill: './frontend/src/polyfill.js',
},
outdir: staticDir,
plugins: [ plugins: [
sassPlugin(), sassPlugin(),
svgPlugin({ svgPlugin({
bundleFile: './svg-bundle.svg', minify: true,
bundleUrl: './svg-bundle.svg', // bundleFile: `${wellKnownDir}/svg-bundle.svg`,
// bundleUrl: `${wellKnownDir}/svg-bundle.svg`,
}), }),
copy({ copy({
resolveFrom: 'cwd', resolveFrom: 'cwd',
assets: { assets: {
from: [ './assets/favicons/*' ], from: [ './assets/favicons/*' ],
to: [ './static' ], to: [ `${wellKnownDir}/de-de-formal`, `${wellKnownDir}/en-eu` ],
}, },
}), }),
copy({ copy({
resolveFrom: 'cwd', resolveFrom: 'cwd',
assets: { assets: {
from: [ './config/robots.txt' ], from: [ './config/robots.txt' ],
to: [ './static' ], to: [ `${wellKnownDir}/de-de-formal`, `${wellKnownDir}/en-eu` ],
}, },
}), }),
// ...['de-de-formal','en-eu'].map((lang) => manifestPlugin({
manifestPlugin({
filename: 'manifest.json',
path: 'config',
// metadata: { timestamp: new Date(), module: 'myapp', type: 'esm', },
processOutput(assets) {
const orderAssets = {
main: assets['main'],
...assets
};
return JSON.stringify(orderAssets, null, ' ');
},
}),
/* copyWithHashPlugin({
patterns: [
'./assets/favicons/*',
'./config/robots.txt',
],
}), */
], ],
entryPoints: [
'./frontend/src/main.js',
'./frontend/src/polyfill.js',
],
bundle: true,
minify: true,
outdir: './static',
}); });

343
package-lock.json generated
View File

@ -35,6 +35,7 @@
"@babel/plugin-transform-private-property-in-object": "^7.24.7", "@babel/plugin-transform-private-property-in-object": "^7.24.7",
"@babel/plugin-transform-runtime": "^7.25.4", "@babel/plugin-transform-runtime": "^7.25.4",
"@babel/preset-env": "^7.25.4", "@babel/preset-env": "^7.25.4",
"@enonic/esbuild-plugin-copy-with-hash": "^0.2.0",
"@eslint/js": "^9.9.1", "@eslint/js": "^9.9.1",
"@fortawesome/fontawesome-free": "^6.6.0", "@fortawesome/fontawesome-free": "^6.6.0",
"autoprefixer": "^10.4.20", "autoprefixer": "^10.4.20",
@ -45,7 +46,11 @@
"css-loader": "^7.1.2", "css-loader": "^7.1.2",
"css-minimizer-webpack-plugin": "^7.0.0", "css-minimizer-webpack-plugin": "^7.0.0",
"esbuild": "0.24.0", "esbuild": "0.24.0",
"esbuild-plugin-assets-manifest": "^1.0.8",
"esbuild-plugin-copy": "2.1.1", "esbuild-plugin-copy": "2.1.1",
"esbuild-plugin-inline-import": "^1.1.0",
"esbuild-plugin-manifest": "1.0.4",
"esbuild-plugin-svg": "^0.1.0",
"esbuild-plugin-svg-bundle": "1.3.0", "esbuild-plugin-svg-bundle": "1.3.0",
"esbuild-sass-plugin": "3.3.1", "esbuild-sass-plugin": "3.3.1",
"eslint": "^9.9.1", "eslint": "^9.9.1",
@ -2009,6 +2014,72 @@
"license": "(Apache-2.0 AND BSD-3-Clause)", "license": "(Apache-2.0 AND BSD-3-Clause)",
"peer": true "peer": true
}, },
"node_modules/@cjs-exporter/globby": {
"version": "13.1.3",
"resolved": "https://registry.npmjs.org/@cjs-exporter/globby/-/globby-13.1.3.tgz",
"integrity": "sha512-rxSVbUhNmIYTZKzrtb6cJIUfFk0PDxzeR4+dzi8edKYmmbqRBO/k7N+tW///QmtY6OznemXiLMSZ20E6DHegxQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"globby": "13.1.3"
}
},
"node_modules/@cjs-exporter/globby/node_modules/dir-glob": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
"integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
"dev": true,
"license": "MIT",
"dependencies": {
"path-type": "^4.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/@cjs-exporter/globby/node_modules/globby": {
"version": "13.1.3",
"resolved": "https://registry.npmjs.org/globby/-/globby-13.1.3.tgz",
"integrity": "sha512-8krCNHXvlCgHDpegPzleMq07yMYTO2sXKASmZmquEYWEmCx6J5UTRbp5RwMJkTJGtcQ44YpiUYUiN0b9mzy8Bw==",
"dev": true,
"license": "MIT",
"dependencies": {
"dir-glob": "^3.0.1",
"fast-glob": "^3.2.11",
"ignore": "^5.2.0",
"merge2": "^1.4.1",
"slash": "^4.0.0"
},
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/@cjs-exporter/globby/node_modules/path-type": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/@cjs-exporter/globby/node_modules/slash": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz",
"integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/@colors/colors": { "node_modules/@colors/colors": {
"version": "1.5.0", "version": "1.5.0",
"resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz",
@ -3077,6 +3148,21 @@
"node": ">=10.0.0" "node": ">=10.0.0"
} }
}, },
"node_modules/@enonic/esbuild-plugin-copy-with-hash": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/@enonic/esbuild-plugin-copy-with-hash/-/esbuild-plugin-copy-with-hash-0.2.0.tgz",
"integrity": "sha512-2KX60dKlPx5Q7d3Jz+23hekbHrCTZzvlZC4U+rLZiP3GMbumEq5lmocw5XKLNTvHtRwNlUze+3TwiXSCD46EeA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@cjs-exporter/globby": "^13.1.3",
"colorette": "^2.0.20",
"xxh3-ts": "^1.0.6"
},
"peerDependencies": {
"esbuild": "~0"
}
},
"node_modules/@esbuild/aix-ppc64": { "node_modules/@esbuild/aix-ppc64": {
"version": "0.24.0", "version": "0.24.0",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.0.tgz", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.0.tgz",
@ -4816,8 +4902,7 @@
"url": "https://feross.org/support" "url": "https://feross.org/support"
} }
], ],
"license": "MIT", "license": "MIT"
"peer": true
}, },
"node_modules/base64id": { "node_modules/base64id": {
"version": "2.0.0", "version": "2.0.0",
@ -6287,6 +6372,23 @@
"postcss": "^8.4" "postcss": "^8.4"
} }
}, },
"node_modules/css-select": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz",
"integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"boolbase": "^1.0.0",
"css-what": "^6.0.1",
"domhandler": "^4.3.1",
"domutils": "^2.8.0",
"nth-check": "^2.0.1"
},
"funding": {
"url": "https://github.com/sponsors/fb55"
}
},
"node_modules/css-tree": { "node_modules/css-tree": {
"version": "2.3.1", "version": "2.3.1",
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz",
@ -6840,6 +6942,21 @@
"void-elements": "^2.0.0" "void-elements": "^2.0.0"
} }
}, },
"node_modules/dom-serializer": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
"integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==",
"dev": true,
"license": "MIT",
"dependencies": {
"domelementtype": "^2.0.1",
"domhandler": "^4.2.0",
"entities": "^2.0.0"
},
"funding": {
"url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
}
},
"node_modules/domain-browser": { "node_modules/domain-browser": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz",
@ -6865,6 +6982,37 @@
], ],
"license": "BSD-2-Clause" "license": "BSD-2-Clause"
}, },
"node_modules/domhandler": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz",
"integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"domelementtype": "^2.2.0"
},
"engines": {
"node": ">= 4"
},
"funding": {
"url": "https://github.com/fb55/domhandler?sponsor=1"
}
},
"node_modules/domutils": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
"integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"dom-serializer": "^1.0.1",
"domelementtype": "^2.2.0",
"domhandler": "^4.2.0"
},
"funding": {
"url": "https://github.com/fb55/domutils?sponsor=1"
}
},
"node_modules/duplexer2": { "node_modules/duplexer2": {
"version": "0.1.4", "version": "0.1.4",
"resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz",
@ -7072,6 +7220,16 @@
"node": ">= 0.4" "node": ">= 0.4"
} }
}, },
"node_modules/entities": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
"integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
"dev": true,
"license": "BSD-2-Clause",
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/env-paths": { "node_modules/env-paths": {
"version": "2.2.1", "version": "2.2.1",
"resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
@ -7282,6 +7440,16 @@
"@esbuild/win32-x64": "0.24.0" "@esbuild/win32-x64": "0.24.0"
} }
}, },
"node_modules/esbuild-plugin-assets-manifest": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/esbuild-plugin-assets-manifest/-/esbuild-plugin-assets-manifest-1.0.8.tgz",
"integrity": "sha512-2goWUmBLpqaHASkMQIgsSqcAs3Y1rDMKcOjk8cWOTtAzmbHYQLh77MEjuQYDx3bBEQ9dVZCaU0/cBIjhvdJf+w==",
"dev": true,
"license": "MIT",
"peerDependencies": {
"esbuild": "*"
}
},
"node_modules/esbuild-plugin-copy": { "node_modules/esbuild-plugin-copy": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/esbuild-plugin-copy/-/esbuild-plugin-copy-2.1.1.tgz", "resolved": "https://registry.npmjs.org/esbuild-plugin-copy/-/esbuild-plugin-copy-2.1.1.tgz",
@ -7453,6 +7621,36 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/esbuild-plugin-inline-import": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/esbuild-plugin-inline-import/-/esbuild-plugin-inline-import-1.1.0.tgz",
"integrity": "sha512-b0xX4tPKBdRjX1CkzpnULpEdeTo9vxD+wf83PKvgUYnOEaJfVxLey4q+sfTUPAdDnRRYasJsQUgQW7/e2Gm5Dw==",
"dev": true,
"license": "MIT"
},
"node_modules/esbuild-plugin-manifest": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/esbuild-plugin-manifest/-/esbuild-plugin-manifest-1.0.4.tgz",
"integrity": "sha512-z+aDPuNIFZto5cYZhZwYk+aWhSqi8yShxx6Yfz+gqMKu+mIrYRtJztxoI2GNIv9hcsEatn+uInkEbf20kTZLxA==",
"dev": true,
"license": "MIT",
"dependencies": {
"proper-lockfile": "^4.1.2"
},
"peerDependencies": {
"esbuild": "~0"
}
},
"node_modules/esbuild-plugin-svg": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/esbuild-plugin-svg/-/esbuild-plugin-svg-0.1.0.tgz",
"integrity": "sha512-/9ZhvIpl+Ovl6glVK3BedvIwrOwSQnECw4Fy6ZwysWib3Ns7UkX6WNGjMOWtvQ1Cnm0uc7sptiKGm0BthKCAJA==",
"dev": true,
"license": "ISC",
"dependencies": {
"svgo": "^2.2.2"
}
},
"node_modules/esbuild-plugin-svg-bundle": { "node_modules/esbuild-plugin-svg-bundle": {
"version": "1.3.0", "version": "1.3.0",
"resolved": "https://registry.npmjs.org/esbuild-plugin-svg-bundle/-/esbuild-plugin-svg-bundle-1.3.0.tgz", "resolved": "https://registry.npmjs.org/esbuild-plugin-svg-bundle/-/esbuild-plugin-svg-bundle-1.3.0.tgz",
@ -7514,6 +7712,72 @@
"url": "https://github.com/fb55/entities?sponsor=1" "url": "https://github.com/fb55/entities?sponsor=1"
} }
}, },
"node_modules/esbuild-plugin-svg/node_modules/commander": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
"integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 10"
}
},
"node_modules/esbuild-plugin-svg/node_modules/css-tree": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz",
"integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"mdn-data": "2.0.14",
"source-map": "^0.6.1"
},
"engines": {
"node": ">=8.0.0"
}
},
"node_modules/esbuild-plugin-svg/node_modules/csso": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz",
"integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==",
"dev": true,
"license": "MIT",
"dependencies": {
"css-tree": "^1.1.2"
},
"engines": {
"node": ">=8.0.0"
}
},
"node_modules/esbuild-plugin-svg/node_modules/mdn-data": {
"version": "2.0.14",
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz",
"integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==",
"dev": true,
"license": "CC0-1.0"
},
"node_modules/esbuild-plugin-svg/node_modules/svgo": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz",
"integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@trysound/sax": "0.2.0",
"commander": "^7.2.0",
"css-select": "^4.1.3",
"css-tree": "^1.1.3",
"csso": "^4.2.0",
"picocolors": "^1.0.0",
"stable": "^0.1.8"
},
"bin": {
"svgo": "bin/svgo"
},
"engines": {
"node": ">=10.13.0"
}
},
"node_modules/esbuild-sass-plugin": { "node_modules/esbuild-sass-plugin": {
"version": "3.3.1", "version": "3.3.1",
"resolved": "https://registry.npmjs.org/esbuild-sass-plugin/-/esbuild-sass-plugin-3.3.1.tgz", "resolved": "https://registry.npmjs.org/esbuild-sass-plugin/-/esbuild-sass-plugin-3.3.1.tgz",
@ -8934,8 +9198,7 @@
"url": "https://feross.org/support" "url": "https://feross.org/support"
} }
], ],
"license": "BSD-3-Clause", "license": "BSD-3-Clause"
"peer": true
}, },
"node_modules/ignore": { "node_modules/ignore": {
"version": "5.3.2", "version": "5.3.2",
@ -15391,6 +15654,25 @@
"license": "MIT", "license": "MIT",
"peer": true "peer": true
}, },
"node_modules/proper-lockfile": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz",
"integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==",
"dev": true,
"license": "MIT",
"dependencies": {
"graceful-fs": "^4.2.4",
"retry": "^0.12.0",
"signal-exit": "^3.0.2"
}
},
"node_modules/proper-lockfile/node_modules/signal-exit": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
"dev": true,
"license": "ISC"
},
"node_modules/proxy-from-env": { "node_modules/proxy-from-env": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
@ -15907,6 +16189,16 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/retry": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
"integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 4"
}
},
"node_modules/reusify": { "node_modules/reusify": {
"version": "1.0.4", "version": "1.0.4",
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
@ -17015,6 +17307,14 @@
"dev": true, "dev": true,
"license": "CC0-1.0" "license": "CC0-1.0"
}, },
"node_modules/stable": {
"version": "0.1.8",
"resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz",
"integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==",
"deprecated": "Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility",
"dev": true,
"license": "MIT"
},
"node_modules/statuses": { "node_modules/statuses": {
"version": "1.5.0", "version": "1.5.0",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
@ -19055,6 +19355,41 @@
"node": ">=0.4" "node": ">=0.4"
} }
}, },
"node_modules/xxh3-ts": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/xxh3-ts/-/xxh3-ts-1.0.6.tgz",
"integrity": "sha512-mFdXT/i6svQNNmwICmszSfKgnYBGBIaCKzPxYHfpM6e1hxYX2gfGPrI9mKRzSnFpi6ieBIK88TGlaudcPYZW5g==",
"dev": true,
"license": "BSD-2 Clause",
"dependencies": {
"buffer": "^6.0.3"
}
},
"node_modules/xxh3-ts/node_modules/buffer": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT",
"dependencies": {
"base64-js": "^1.3.1",
"ieee754": "^1.2.1"
}
},
"node_modules/y18n": { "node_modules/y18n": {
"version": "5.0.8", "version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",

View File

@ -21,6 +21,7 @@
"@babel/plugin-transform-private-property-in-object": "^7.24.7", "@babel/plugin-transform-private-property-in-object": "^7.24.7",
"@babel/plugin-transform-runtime": "^7.25.4", "@babel/plugin-transform-runtime": "^7.25.4",
"@babel/preset-env": "^7.25.4", "@babel/preset-env": "^7.25.4",
"@enonic/esbuild-plugin-copy-with-hash": "^0.2.0",
"@eslint/js": "^9.9.1", "@eslint/js": "^9.9.1",
"@fortawesome/fontawesome-free": "^6.6.0", "@fortawesome/fontawesome-free": "^6.6.0",
"autoprefixer": "^10.4.20", "autoprefixer": "^10.4.20",
@ -31,7 +32,11 @@
"css-loader": "^7.1.2", "css-loader": "^7.1.2",
"css-minimizer-webpack-plugin": "^7.0.0", "css-minimizer-webpack-plugin": "^7.0.0",
"esbuild": "0.24.0", "esbuild": "0.24.0",
"esbuild-plugin-assets-manifest": "^1.0.8",
"esbuild-plugin-copy": "2.1.1", "esbuild-plugin-copy": "2.1.1",
"esbuild-plugin-inline-import": "^1.1.0",
"esbuild-plugin-manifest": "1.0.4",
"esbuild-plugin-svg": "^0.1.0",
"esbuild-plugin-svg-bundle": "1.3.0", "esbuild-plugin-svg-bundle": "1.3.0",
"esbuild-sass-plugin": "3.3.1", "esbuild-sass-plugin": "3.3.1",
"eslint": "^9.9.1", "eslint": "^9.9.1",
@ -80,6 +85,7 @@
"whatwg-fetch": "^3.6.20" "whatwg-fetch": "^3.6.20"
}, },
"scripts": { "scripts": {
"build": "node esbuild.config.mjs" "build": "node esbuild.config.mjs",
"start": "node esbuild.config.mjs --watch"
} }
} }

5
routes
View File

@ -1,4 +1,4 @@
-- SPDX-FileCopyrightText: 2022 Gregor Kleen <gregor.kleen@ifi.lmu.de>,Sarah Vaupel <sarah.vaupel@ifi.lmu.de>,Steffen Jost <jost@tcs.ifi.lmu.de>,Wolfgang Witt <Wolfgang.Witt@campus.lmu.de> -- SPDX-FileCopyrightText: 2022-2024 Sarah Vaupel <sarah.vaupel@uniworx.de>, Gregor Kleen <gregor.kleen@ifi.lmu.de>, Sarah Vaupel <sarah.vaupel@ifi.lmu.de>, Steffen Jost <jost@tcs.ifi.lmu.de>, Wolfgang Witt <Wolfgang.Witt@campus.lmu.de>
-- --
-- SPDX-License-Identifier: AGPL-3.0-or-later -- SPDX-License-Identifier: AGPL-3.0-or-later
@ -44,6 +44,7 @@
-- !development -- like free, but only for development builds -- !development -- like free, but only for development builds
/static StaticR EmbeddedStatic appStatic !free /static StaticR EmbeddedStatic appStatic !free
/auth AuthR Auth getAuth !free /auth AuthR Auth getAuth !free
/metrics MetricsR GET !free -- verify if this can be free /metrics MetricsR GET !free -- verify if this can be free
@ -297,4 +298,4 @@
/swagger SwaggerR GET !free /swagger SwaggerR GET !free
/swagger.json SwaggerJsonR GET !free /swagger.json SwaggerJsonR GET !free
!/*WellKnownFileName WellKnownR GET !free !/*WellKnownFileName WellKnownR GET !free

View File

@ -1,4 +1,4 @@
-- SPDX-FileCopyrightText: 2022 Gregor Kleen <gregor.kleen@ifi.lmu.de>,Sarah Vaupel <sarah.vaupel@ifi.lmu.de>,Steffen Jost <jost@tcs.ifi.lmu.de>,Wolfgang Witt <Wolfgang.Witt@campus.lmu.de> -- SPDX-FileCopyrightText: 2022-2024 Sarah Vaupel <sarah.vaupel@uniworx.de>, Gregor Kleen <gregor.kleen@ifi.lmu.de>,Sarah Vaupel <sarah.vaupel@ifi.lmu.de>,Steffen Jost <jost@tcs.ifi.lmu.de>,Wolfgang Witt <Wolfgang.Witt@campus.lmu.de>
-- --
-- SPDX-License-Identifier: AGPL-3.0-or-later -- SPDX-License-Identifier: AGPL-3.0-or-later
@ -91,7 +91,9 @@ instance Yesod UniWorX where
isAuthorized :: HasCallStack => Route UniWorX -> Bool -> HandlerFor UniWorX AuthResult isAuthorized :: HasCallStack => Route UniWorX -> Bool -> HandlerFor UniWorX AuthResult
isAuthorized r w = runDBRead $ evalAccess r w isAuthorized r w = runDBRead $ evalAccess r w
-- TODO: replace memcached-based static-content caching with basic addStaticContent
addStaticContent = UniWorX.addStaticContent addStaticContent = UniWorX.addStaticContent
-- addStaticContent = embedStaticContent appStatic StaticR Right -- TODO: minify on production builds
fileUpload _site _length = FileUploadMemory lbsBackEnd fileUpload _site _length = FileUploadMemory lbsBackEnd

View File

@ -1,4 +1,4 @@
-- SPDX-FileCopyrightText: 2022 Gregor Kleen <gregor.kleen@ifi.lmu.de>,Sarah Vaupel <sarah.vaupel@ifi.lmu.de>,Steffen Jost <jost@cip.ifi.lmu.de>,Winnie Ros <winnie.ros@campus.lmu.de>,Wolfgang Witt <Wolfgang.Witt@campus.lmu.de> -- SPDX-FileCopyrightText: 2022-2024 Sarah Vaupel <sarah.vaupel@uniworx.de>, Gregor Kleen <gregor.kleen@ifi.lmu.de>,Sarah Vaupel <sarah.vaupel@ifi.lmu.de>,Steffen Jost <jost@cip.ifi.lmu.de>,Winnie Ros <winnie.ros@campus.lmu.de>,Wolfgang Witt <Wolfgang.Witt@campus.lmu.de>
-- --
-- SPDX-License-Identifier: AGPL-3.0-or-later -- SPDX-License-Identifier: AGPL-3.0-or-later
@ -488,7 +488,8 @@ siteLayout' overrideHeading widget = do
frontendDatetimeLocale <- toJSON <$> selectLanguage frontendDatetimeLocales frontendDatetimeLocale <- toJSON <$> selectLanguage frontendDatetimeLocales
pc <- widgetToPageContent $ do pc <- widgetToPageContent $ do
webpackLinks_main StaticR bundlerLinks_main StaticR
bundlerLinks_polyfill StaticR
toWidget $(juliusFile "templates/i18n.julius") toWidget $(juliusFile "templates/i18n.julius")
whenIsJust currentApproot' $ \currentApproot -> whenIsJust currentApproot' $ \currentApproot ->
toWidget $(juliusFile "templates/approot.julius") toWidget $(juliusFile "templates/approot.julius")

View File

@ -1,4 +1,4 @@
-- SPDX-FileCopyrightText: 2022 Gregor Kleen <gregor.kleen@ifi.lmu.de>,Sarah Vaupel <sarah.vaupel@ifi.lmu.de>,Steffen Jost <jost@tcs.ifi.lmu.de> -- SPDX-FileCopyrightText: 2022-2024 Sarah Vaupel <sarah.vaupel@uniworx.de>, Gregor Kleen <gregor.kleen@ifi.lmu.de>,Sarah Vaupel <sarah.vaupel@ifi.lmu.de>,Steffen Jost <jost@tcs.ifi.lmu.de>
-- --
-- SPDX-License-Identifier: AGPL-3.0-or-later -- SPDX-License-Identifier: AGPL-3.0-or-later
@ -90,7 +90,7 @@ import qualified Data.Set as Set
data AppSettings = AppSettings data AppSettings = AppSettings
{ appStaticDir :: FilePath { appStaticDir :: FilePath
-- ^ Directory from which to serve static files. -- ^ Directory from which to serve static files.
, appWebpackEntrypoints :: FilePath , appBundlerEntrypoints :: FilePath
, appWellKnownDir :: FilePath , appWellKnownDir :: FilePath
, appWellKnownLinkFile :: FilePath , appWellKnownLinkFile :: FilePath
, appDatabaseConf :: PostgresConf , appDatabaseConf :: PostgresConf
@ -624,7 +624,7 @@ instance FromJSON AppSettings where
appStaticDir <- o .: "static-dir" appStaticDir <- o .: "static-dir"
appWellKnownDir <- o .: "well-known-dir" appWellKnownDir <- o .: "well-known-dir"
appWellKnownLinkFile <- o .: "well-known-link-file" appWellKnownLinkFile <- o .: "well-known-link-file"
appWebpackEntrypoints <- o .: "webpack-manifest" appBundlerEntrypoints <- o .: "bundler-manifest"
appDatabaseConf <- o .: "database" appDatabaseConf <- o .: "database"
appAutoDbMigrate <- o .: "auto-db-migrate" appAutoDbMigrate <- o .: "auto-db-migrate"
let nonEmptyHost LdapConf{..} = case ldapHost of let nonEmptyHost LdapConf{..} = case ldapHost of

View File

@ -1,4 +1,4 @@
-- SPDX-FileCopyrightText: 2022 Gregor Kleen <gregor.kleen@ifi.lmu.de>,Steffen Jost <jost@cip.ifi.lmu.de>,Steffen Jost <jost@tcs.ifi.lmu.de> -- SPDX-FileCopyrightText: 2022-2024 Sarah Vaupel <sarah.vaupel@uniworx.de>, Gregor Kleen <gregor.kleen@ifi.lmu.de>, Steffen Jost <jost@cip.ifi.lmu.de>, Steffen Jost <jost@tcs.ifi.lmu.de>
-- --
-- SPDX-License-Identifier: AGPL-3.0-or-later -- SPDX-License-Identifier: AGPL-3.0-or-later
@ -7,16 +7,16 @@
-- prevents rebuilds if files change, that are not directly used (like -- prevents rebuilds if files change, that are not directly used (like
-- webpack bundles) -- webpack bundles)
module Settings.StaticFiles module Settings.StaticFiles
( webpackLinks_main ( bundlerLinks_main, bundlerLinks_polyfill
, embeddedStatic , embeddedStatic
, module Yesod.EmbeddedStatic , module Yesod.EmbeddedStatic
) where ) where
import ClassyPrelude.Yesod import ClassyPrelude.Yesod
import Settings (appStaticDir, appWebpackEntrypoints, compileTimeAppSettings) import Settings (appStaticDir, appBundlerEntrypoints, compileTimeAppSettings)
import Settings.StaticFiles.Generator import Settings.StaticFiles.Generator
import Settings.StaticFiles.Webpack import Settings.StaticFiles.Bundler
import Yesod.EmbeddedStatic import Yesod.EmbeddedStatic
-- This generates easy references to files in the static directory at compile time, -- This generates easy references to files in the static directory at compile time,
@ -35,4 +35,4 @@ import Yesod.EmbeddedStatic
#endif #endif
mkEmbeddedStatic DEV_BOOL "embeddedStatic" . pure . staticGenerator $ appStaticDir compileTimeAppSettings mkEmbeddedStatic DEV_BOOL "embeddedStatic" . pure . staticGenerator $ appStaticDir compileTimeAppSettings
mkWebpackEntrypoints (appWebpackEntrypoints compileTimeAppSettings) (pure staticGenerator) $ appStaticDir compileTimeAppSettings mkBundlerEntrypoints (appBundlerEntrypoints compileTimeAppSettings) (pure staticGenerator) $ appStaticDir compileTimeAppSettings

View File

@ -1,9 +1,9 @@
-- SPDX-FileCopyrightText: 2022 Gregor Kleen <gregor.kleen@ifi.lmu.de> -- SPDX-FileCopyrightText: 2022-2024 Sarah Vaupel <sarah.vaupel@uniworx.de>, Gregor Kleen <gregor.kleen@ifi.lmu.de>
-- --
-- SPDX-License-Identifier: AGPL-3.0-or-later -- SPDX-License-Identifier: AGPL-3.0-or-later
module Settings.StaticFiles.Webpack module Settings.StaticFiles.Bundler
( mkWebpackEntrypoints ( mkBundlerEntrypoints
) where ) where
import ClassyPrelude import ClassyPrelude
@ -12,7 +12,7 @@ import Language.Haskell.TH
import Language.Haskell.TH.Syntax hiding (Lift(..)) import Language.Haskell.TH.Syntax hiding (Lift(..))
import qualified Language.Haskell.TH.Syntax as TH (Lift(..)) import qualified Language.Haskell.TH.Syntax as TH (Lift(..))
import qualified Data.Yaml as Yaml import qualified Data.Aeson as Aeson
import qualified Data.Map as Map import qualified Data.Map as Map
@ -25,7 +25,6 @@ import Control.Lens.Indexed (iforM)
import Control.Monad.Writer.Class (MonadWriter(..)) import Control.Monad.Writer.Class (MonadWriter(..))
import Control.Monad.Trans.Writer.Lazy (execWriterT) import Control.Monad.Trans.Writer.Lazy (execWriterT)
import Control.Monad.Catch (MonadThrow(..))
import System.FilePath (makeRelative) import System.FilePath (makeRelative)
@ -35,11 +34,11 @@ import Utils ()
import Data.Containers.ListUtils import Data.Containers.ListUtils
mkWebpackEntrypoints :: FilePath -- ^ Path to YAML-manifest mkBundlerEntrypoints :: FilePath -- ^ Path to manifest (json)
-> [FilePath -> Generator] -> [FilePath -> Generator]
-> FilePath -- ^ Path to static dir -> FilePath -- ^ Path to static dir
-> DecsQ -> DecsQ
mkWebpackEntrypoints manifest mkGen stDir = do mkBundlerEntrypoints manifest mkGen stDir = do
addDependentFile manifest addDependentFile manifest
entrypoints <- decodeManifest manifest entrypoints <- decodeManifest manifest
@ -54,14 +53,14 @@ mkWebpackEntrypoints manifest mkGen stDir = do
, ebLocation entry , ebLocation entry
, "” of file “" , "” of file “"
, file , file
, "” of webpack entrypoint “" , "” of bundler entrypoint “"
, entrypoint , entrypoint
, "” has no haskellName" , "” has no haskellName"
] ]
Just n -> tell $ pure (n, ebMimeType entry) Just n -> tell $ pure (n, ebMimeType entry)
let entryName = mkName $ "webpackEntrypoint_" <> entrypoint let entryName = mkName $ "bundlerEntrypoint_" <> entrypoint
widgetName = mkName $ "webpackLinks_" <> entrypoint widgetName = mkName $ "bundlerLinks_" <> entrypoint
staticR <- newName "staticR" staticR <- newName "staticR"
sequence sequence
@ -82,16 +81,12 @@ mkWebpackEntrypoints manifest mkGen stDir = do
| mime `ctEq` "application/javascript" | mime `ctEq` "application/javascript"
-> addScript $ $(varE staticR) sRoute -> addScript $ $(varE staticR) sRoute
| otherwise | otherwise
-> $logErrorS "siteLayout" [st|Unknown mime type in webpack bundle: #{tshow mime}|] -> $logErrorS "siteLayout" [st|Unknown mime type in bundle: #{tshow mime}|]
|]) [] |]) []
] ]
] ]
where
decodeManifest :: FilePath -> Q (Map String [FilePath])
decodeManifest manifest' = do
res <- liftIO $ Yaml.decodeFileWithWarnings manifest'
case res of
Left exc -> throwM exc
Right (ws, res') -> res' <$ mapM_ (\w -> reportWarning $ "Warning while parsing webpack manifest: " <> show w) ws
decodeManifest :: FilePath -> Q (Map String (Map String FilePath))
decodeManifest manifest' = liftIO (Aeson.eitherDecodeFileStrict manifest') >>= \case
Left exc -> error $ "Encountered error while decoding manifest: " ++ exc
Right res -> return res

View File

@ -1,4 +1,4 @@
-- SPDX-FileCopyrightText: 2022 Gregor Kleen <gregor.kleen@ifi.lmu.de> -- SPDX-FileCopyrightText: 2022-2024 Sarah Vaupel <sarah.vaupel@uniworx.de>, Gregor Kleen <gregor.kleen@ifi.lmu.de>
-- --
-- SPDX-License-Identifier: AGPL-3.0-or-later -- SPDX-License-Identifier: AGPL-3.0-or-later
@ -58,7 +58,7 @@ nwellKnownHtmlLinks = mkName "wellKnownHtmlLinks"
mkWellKnown :: Lang -- ^ Default language mkWellKnown :: Lang -- ^ Default language
-> FilePath -- ^ Base directory -> FilePath -- ^ Base directory
-> FilePath -- ^ Link file (@html_code.html@) -> FilePath -- ^ Link file (@include.html@)
-> DecsQ -> DecsQ
mkWellKnown defLang wellKnownBase wellKnownLinks = do mkWellKnown defLang wellKnownBase wellKnownLinks = do
inputFiles <- fmap dirTree . liftIO $ readDirectoryWith (\f -> (f, ) <$> BS.readFile f) wellKnownBase inputFiles <- fmap dirTree . liftIO $ readDirectoryWith (\f -> (f, ) <$> BS.readFile f) wellKnownBase
@ -79,7 +79,7 @@ mkWellKnown defLang wellKnownBase wellKnownLinks = do
return $ file & _1 %~ makeRelative (wellKnownBase </> language) return $ file & _1 %~ makeRelative (wellKnownBase </> language)
return (Text.pack language, lContents') return (Text.pack language, lContents')
| otherwise | otherwise
-> fail "wellKnownBase is not a directory" -> fail "well-known does not exist or is not a directory"
fLanguages <- if fLanguages <- if
| defLang `Set.member` Map.keysSet languageFiles | defLang `Set.member` Map.keysSet languageFiles
@ -103,7 +103,7 @@ mkWellKnown defLang wellKnownBase wellKnownLinks = do
c <- lContents' c <- lContents'
return (Text.pack language, c) return (Text.pack language, c)
| otherwise | otherwise
-> fail "wellKnownBase is not a directory" -> fail "well-known does not exist or is not a directory"
lLanguages <- if lLanguages <- if
| defLang `Set.member` Map.keysSet languageLinks | defLang `Set.member` Map.keysSet languageLinks