chore(frontend): remove custom Webpack sqlite-wasm integration plugins and cleanup config

- Deleted `dummy.js` and its usage for sqlite-wasm integration as custom Webpack adjustments are no longer necessary.
- Removed outdated Webpack configuration files: `ignore-sqlite-wasm.js`, `ignore-sqlite-wasm-critical-dependency.js`, and `sqljs-fix.js`.
- Introduced `sqlite-config.js` for simplified and streamlined sqlite-wasm and Skiko Webpack configuration.
- Minor code formatting adjustments across frontend modules for improved consistency.
This commit is contained in:
2026-01-26 20:37:23 +01:00
parent 29ad73b508
commit 3e587381ed
30 changed files with 1535 additions and 941 deletions
@@ -51,8 +51,6 @@ kotlin {
}
// Browser-Tests komplett deaktivieren (Configuration Cache kompatibel)
testTask {
// enabled = false
useKarma {
useChromeHeadless()
environment("CHROME_BIN", "/usr/bin/google-chrome-stable")
@@ -62,15 +60,6 @@ kotlin {
binaries.executable()
}
// Wasm vorerst deaktiviert
/*
@OptIn(org.jetbrains.kotlin.gradle.ExperimentalWasmDsl::class)
wasmJs {
browser()
binaries.executable()
}
*/
sourceSets {
commonMain.dependencies {
// Shared modules
@@ -114,18 +103,6 @@ kotlin {
implementation(devNpm("copy-webpack-plugin", "11.0.0"))
}
/*
val wasmJsMain = getByName("wasmJsMain")
wasmJsMain.dependencies {
implementation(libs.ktor.client.js) // WASM verwendet JS-Client [cite: 7]
// Compose für shared UI components für WASM
implementation(compose.runtime)
implementation(compose.foundation)
implementation(compose.material3)
}
*/
commonTest.dependencies {
implementation(libs.kotlin.test)
}
@@ -136,104 +113,28 @@ kotlin {
// SQLDelight WebWorker (OPFS) resource
// ---------------------------------------------------------------------------
// `:frontend:core:local-db` ships `sqlite.worker.js` as a JS resource.
// When bundling the final JS app, webpack resolves `new URL("sqlite.worker.js", import.meta.url)`
// relative to the Kotlin JS package folder (root build dir). We therefore copy the worker into
// that folder before webpack runs.
// We need to ensure this worker file is available in the output directory so the browser can load it.
// The WASM file itself is handled by Webpack (via CopyWebpackPlugin in webpack.config.d/sqlite-config.js).
// HACK: Overwrite sqlite3.wasm in node_modules with a dummy JS file to fool Webpack
val patchSqliteWasmInNodeModules by tasks.registering(Copy::class) {
dependsOn(rootProject.tasks.named("kotlinNpmInstall"))
val copySqliteWorkerToWebpackSource by tasks.registering(Copy::class) {
val localDb = project(":frontend:core:local-db")
dependsOn(localDb.tasks.named("jsProcessResources"))
// We take our dummy.js
from(localDb.layout.buildDirectory.file("processedResources/js/main/dummy.js")) {
rename { "sqlite3.wasm" } // Rename it to sqlite3.wasm
}
// And copy it OVER the original wasm file in node_modules
into(rootProject.layout.buildDirectory.dir("js/node_modules/@sqlite.org/sqlite-wasm/sqlite-wasm/jswasm"))
// Force overwrite
duplicatesStrategy = DuplicatesStrategy.INCLUDE
}
val copySqliteAssetsToWebpackSource by tasks.registering(Copy::class) {
val localDb = project(":frontend:core:local-db")
dependsOn(localDb.tasks.named("jsProcessResources"), rootProject.tasks.named("kotlinNpmInstall"))
// Explicit dependency on the patch task to ensure we copy the REAL wasm file before it gets patched?
// NO! We want to copy the REAL wasm file to the output, but PATCH the one in node_modules.
// So we must copy the real one BEFORE patching.
// But wait, copySqliteAssetsToWebpackSource copies FROM node_modules.
// If we patch node_modules first, we copy the dummy file!
// So: copySqliteAssetsToWebpackSource must run BEFORE patchSqliteWasmInNodeModules?
// Or we copy from a different source (e.g. the original npm package cache? No access).
// Better: We copy the real wasm file from node_modules to a temporary location FIRST,
// then patch node_modules, then copy from temp to output.
// Actually, we can just copy from node_modules BEFORE the patch task runs.
// But Gradle task ordering is tricky.
// Let's change the source of the copy. We can't easily access the original npm package.
// But we know that `kotlinNpmInstall` restores the original files.
// So the order must be:
// 1. kotlinNpmInstall (restores original sqlite3.wasm)
// 2. copySqliteAssetsToWebpackSource (copies original sqlite3.wasm to output)
// 3. patchSqliteWasmInNodeModules (overwrites sqlite3.wasm with dummy.js)
// 4. webpack (uses dummy.js)
mustRunAfter(rootProject.tasks.named("kotlinNpmInstall"))
from(localDb.layout.buildDirectory.file("processedResources/js/main/sqlite.worker.js"))
from(rootProject.layout.buildDirectory.file("js/node_modules/@sqlite.org/sqlite-wasm/sqlite-wasm/jswasm/sqlite3.wasm"))
// Root build directory where Kotlin JS packages are assembled.
// This is one of the directories served by webpack-dev-server for static content.
into(rootProject.layout.buildDirectory.dir("js/packages/${rootProject.name}-frontend-shells-meldestelle-portal/kotlin"))
}
// Additional task to copy the worker and its wasm dependency to the distribution folder (for production build)
val copySqliteAssetsToDist by tasks.registering(Copy::class) {
val localDb = project(":frontend:core:local-db")
dependsOn(localDb.tasks.named("jsProcessResources"), rootProject.tasks.named("kotlinNpmInstall"))
// Same logic here: copy before patch
mustRunAfter(rootProject.tasks.named("kotlinNpmInstall"))
from(localDb.layout.buildDirectory.file("processedResources/js/main/sqlite.worker.js"))
from(rootProject.layout.buildDirectory.file("js/node_modules/@sqlite.org/sqlite-wasm/sqlite-wasm/jswasm/sqlite3.wasm"))
// Copy to the distribution directory where index.html resides
into(layout.buildDirectory.dir("dist/js/productionExecutable"))
}
// Ensure the assets are present for the production bundle.
tasks.named("jsBrowserProductionWebpack") {
dependsOn(copySqliteAssetsToWebpackSource)
dependsOn(patchSqliteWasmInNodeModules)
// Enforce order: Copy real assets first, then patch node_modules
// patchSqliteWasmInNodeModules must run AFTER copySqliteAssetsToWebpackSource
// But wait, dependsOn doesn't guarantee order.
// We need to configure the tasks themselves.
finalizedBy(copySqliteAssetsToDist)
}
// Configure task ordering
patchSqliteWasmInNodeModules {
mustRunAfter(copySqliteAssetsToWebpackSource)
// Removed circular dependency: mustRunAfter(copySqliteAssetsToDist)
}
// Ensure the assets are present for the development bundle.
// Ensure the worker is present for the development bundle.
tasks.named("jsBrowserDevelopmentWebpack") {
dependsOn(copySqliteAssetsToWebpackSource)
dependsOn(patchSqliteWasmInNodeModules)
dependsOn(copySqliteWorkerToWebpackSource)
}
// Ensure the worker is present for the production bundle.
tasks.named("jsBrowserProductionWebpack") {
dependsOn(copySqliteWorkerToWebpackSource)
}
// KMP Compile-Optionen
@@ -253,9 +154,6 @@ tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
// Kotlin/JS source maps
// ---------------------------------------------------------------------------
// Production source maps must remain enabled for browser debugging.
// The remaining Kotlin/Gradle message
// `Cannot rewrite paths in JavaScript source maps: Too many sources or format is not supported`
// is treated as an external Kotlin/JS toolchain limitation and is documented separately.
// Configure a duplicate handling strategy for distribution tasks
tasks.withType<Tar> {
@@ -268,7 +166,7 @@ tasks.withType<Zip> {
// Duplicate-Handling für Distribution
tasks.withType<Copy> {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE // Statt EXCLUDE
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
tasks.withType<Sync> {
@@ -39,6 +39,7 @@ fun MainApp() {
onPrimaryCta = { currentScreen = AppScreen.Login },
onSecondary = { currentScreen = AppScreen.Home }
)
is AppScreen.Home -> WelcomeScreen(
authTokenManager = authTokenManager,
onOpenPing = { currentScreen = AppScreen.Ping },
@@ -169,7 +170,7 @@ private fun LandingScreen(
@Composable
private fun FeatureCard(number: String, title: String, body: String) {
Surface( tonalElevation = 0.dp ) {
Surface(tonalElevation = 0.dp) {
Row(modifier = Modifier.fillMaxWidth()) {
Column(modifier = Modifier.width(56.dp).padding(top = 6.dp)) {
Text(text = number, style = MaterialTheme.typography.titleMedium, color = MaterialTheme.colorScheme.primary)
@@ -1,14 +0,0 @@
// This is a dummy file to satisfy Webpack's requirement for sqlite3.wasm and other modules.
// It mimics the structure of the sqlite3-wasm module to prevent build errors.
// The worker code imports it like this:
// import sqlite3InitModule from '@sqlite.org/sqlite-wasm';
// So we need a default export that is a function.
// This function should mimic the behavior of sqlite3InitModule, which returns a Promise.
export default function dummySqlite3InitModule() {
// Since we are manually loading the WASM binary in the worker, this dummy module
// is primarily here to satisfy Webpack's resolution and prevent errors.
// It doesn't need to actually load the WASM.
return Promise.resolve({});
};
@@ -15,7 +15,7 @@
<script>
// Prefer explicit query param override (?apiBaseUrl=http://host:port),
// then fall back to same-origin. This avoids Docker secrets and works with Nginx proxy.
(function(){
(function () {
try {
const params = new URLSearchParams(window.location.search);
const override = params.get('apiBaseUrl');
@@ -29,13 +29,13 @@
}
})();
// KMP bundle will read globalThis.API_BASE_URL in PlatformConfig.js
</script>
</script>
<script src="web-app.js"></script>
<script>
// Register Service Worker only in non-localhost environments
if ('serviceWorker' in navigator && !['localhost', '127.0.0.1', '::1'].includes(location.hostname)) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('/sw.js').catch(function(err){
window.addEventListener('load', function () {
navigator.serviceWorker.register('/sw.js').catch(function (err) {
console.warn('ServiceWorker registration failed:', err);
});
});
@@ -0,0 +1,15 @@
// Dummy module to satisfy WASI imports in Webpack
// Used for skiko.wasm and potentially others
export function abort() {
console.error("WASI abort called");
}
// Some WASM modules might look for these
export function emscripten_notify_memory_growth() {
}
export default {
abort,
emscripten_notify_memory_growth
};
@@ -1,29 +0,0 @@
// Suppress a known, external webpack warning coming from `@sqlite.org/sqlite-wasm`.
//
// Webpack warning:
// "Critical dependency: the request of a dependency is an expression"
//
// Root cause:
// `@sqlite.org/sqlite-wasm/sqlite-wasm/jswasm/sqlite3.mjs` uses a dynamic Worker URL:
// `new Worker(new URL(options.proxyUri, import.meta.url))`
// which webpack cannot statically analyze.
//
// We keep this suppression максимально spezifisch:
// - match only this warning message
// - and only if it originates from the sqlite-wasm package path.
(function (config) {
config.ignoreWarnings = config.ignoreWarnings || []
// Webpack passes warning objects with `message` and `module.resource`.
config.ignoreWarnings.push((warning) => {
const message = String(warning && warning.message ? warning.message : warning)
if (!message.includes('Critical dependency: the request of a dependency is an expression')) return false
const resource = warning && warning.module && warning.module.resource
? String(warning.module.resource)
: ''
return resource.includes('node_modules/@sqlite.org/sqlite-wasm/')
})
})(config)
@@ -1,104 +0,0 @@
// This file contains Webpack configuration adjustments for WebAssembly modules,
// specifically to handle `skiko.wasm` and `sqlite3.wasm` correctly.
var pathModule;
try {
pathModule = path;
} catch (e) {
pathModule = require('path');
}
var webpackModule;
try {
webpackModule = webpack;
} catch (e) {
webpackModule = require('webpack');
}
// 1. Enable WebAssembly experiments in Webpack 5
config.experiments = config.experiments || {};
config.experiments.asyncWebAssembly = true;
config.module = config.module || {};
config.module.rules = config.module.rules || [];
// 2. Add a rule to correctly handle .wasm files (like skiko.wasm) as WebAssembly modules
config.module.rules.push({
test: /\.wasm$/,
type: 'webassembly/async'
});
// 3. NormalModuleReplacementPlugin to redirect 'sqlite3.wasm' AND other internal sqlite-wasm modules to our dummy JS file.
// This is needed because the `sqlite-wasm` library tries to `require` these files in a Webpack environment.
// We want these `require` calls to return an empty JS object (from dummy.js) instead of failing.
// Our worker will manually fetch the real sqlite3.wasm.
const dummyPath = pathModule.resolve(__dirname, "../../../../frontend/shells/meldestelle-portal/build/processedResources/js/main/dummy.js");
// Redirect sqlite3.wasm
config.plugins.push(
new webpackModule.NormalModuleReplacementPlugin(
/sqlite3\.wasm$/,
dummyPath
)
);
// Redirect other internal sqlite-wasm modules that might be causing issues
// The error log showed: Can't resolve './sqlite-wasm/jswasm/sqlite3.mjs' and 'sqlite3-worker1-promiser.mjs'
// We redirect them to dummy.js as well, assuming we don't need them for our manual loading approach.
// Be careful not to redirect the main entry point if it's needed.
// The errors seem to come from inside the node_modules package trying to resolve relative paths.
// Let's try to be more specific. If these are optional dependencies or part of the node-loading logic,
// replacing them with dummy.js should be fine.
config.plugins.push(
new webpackModule.NormalModuleReplacementPlugin(
/sqlite3\.mjs$/,
function(resource) {
// Only replace if it's inside the sqlite-wasm package structure we want to avoid
if (resource.context.includes('@sqlite.org/sqlite-wasm')) {
resource.request = dummyPath;
}
}
)
);
config.plugins.push(
new webpackModule.NormalModuleReplacementPlugin(
/sqlite3-worker1-promiser\.mjs$/,
dummyPath
)
);
// 4. Handle WASI imports for skiko.wasm (env, wasi_snapshot_preview1)
// Webpack needs to know how to resolve these "magic" imports.
// We can treat them as externals or empty modules.
// Since we are in a browser environment, these are often provided by the runtime or polyfilled.
// Mapping them to false tells Webpack to ignore them (empty module).
config.resolve = config.resolve || {};
config.resolve.fallback = config.resolve.fallback || {};
// Fallbacks for Node.js core modules that might be required by libraries
config.resolve.fallback.fs = false;
config.resolve.fallback.path = false;
config.resolve.fallback.crypto = false;
// Ignore WASI imports
config.ignoreWarnings = config.ignoreWarnings || [];
config.ignoreWarnings.push(/Critical dependency: the request of a dependency is an expression/);
// Use externals to handle WASI imports if fallback doesn't work
config.externals = config.externals || {};
// config.externals['env'] = 'env'; // This might expect a global 'env' variable
// config.externals['wasi_snapshot_preview1'] = 'wasi_snapshot_preview1';
// Better approach for WASI in Webpack 5 with asyncWebAssembly:
// Webpack should handle this if we don't interfere.
// The error "Can't resolve 'env'" suggests it's looking for a module named 'env'.
// We can provide a dummy module for these.
config.resolve.alias = config.resolve.alias || {};
config.resolve.alias['env'] = dummyPath;
config.resolve.alias['wasi_snapshot_preview1'] = dummyPath;
@@ -3,6 +3,6 @@
config.devServer = config.devServer || {};
config.devServer.headers = {
...config.devServer.headers,
"Cross-Origin-Opener-Policy": "same-origin",
"Cross-Origin-Embedder-Policy": "require-corp"
"Cross-Origin-Opener-Policy": "same-origin",
"Cross-Origin-Embedder-Policy": "require-corp"
};
@@ -0,0 +1,93 @@
// Webpack configuration for SQLite WASM support AND Skiko fixes
const CopyWebpackPlugin = require('copy-webpack-plugin');
const webpack = require('webpack');
const path = require('path');
const fs = require('fs');
config.resolve = config.resolve || {};
config.resolve.fallback = config.resolve.fallback || {};
config.resolve.alias = config.resolve.alias || {};
// 1. Fallbacks for Node.js core modules
config.resolve.fallback.fs = false;
config.resolve.fallback.path = false;
config.resolve.fallback.crypto = false;
// 2. Resolve sqlite3 paths
let sqliteBaseDir;
try {
const packagePath = path.dirname(require.resolve('@sqlite.org/sqlite-wasm/package.json'));
sqliteBaseDir = path.join(packagePath, 'sqlite-wasm/jswasm');
} catch (e) {
console.warn("Could not resolve @sqlite.org/sqlite-wasm path automatically. Using fallback path.");
sqliteBaseDir = path.resolve(__dirname, '../../../../../../node_modules/@sqlite.org/sqlite-wasm/sqlite-wasm/jswasm');
}
// 3. Copy ALL sqlite3 assets (wasm, js, and auxiliary workers)
if (fs.existsSync(sqliteBaseDir)) {
console.log("Copying sqlite3 assets from:", sqliteBaseDir);
config.plugins.push(
new CopyWebpackPlugin({
patterns: [
{
from: sqliteBaseDir,
to: '.', // Copy to root of dist
globOptions: {
ignore: ['**/package.json'] // Don't copy package.json if present
},
noErrorOnMissing: true
}
]
})
);
} else {
console.error("ERROR: sqlite3 base directory does not exist:", sqliteBaseDir);
}
// 4. Alias sqlite3.wasm (still needed for some internal checks maybe)
const sqliteWasmPath = path.join(sqliteBaseDir, 'sqlite3.wasm');
config.resolve.alias['sqlite3.wasm'] = sqliteWasmPath;
config.resolve.alias['./sqlite3.wasm'] = sqliteWasmPath;
// 5. Handle .wasm files
config.experiments = config.experiments || {};
config.experiments.asyncWebAssembly = true;
config.module = config.module || {};
config.module.rules = config.module.rules || [];
// Treat Skiko WASM as resource to avoid parsing errors
config.module.rules.push({
test: /skiko\.wasm$/,
type: 'asset/resource'
});
// Treat other WASM as async (default)
config.module.rules.push({
test: /\.wasm$/,
exclude: /skiko\.wasm$/,
type: 'webassembly/async'
});
// 6. Ignore warnings
config.ignoreWarnings = config.ignoreWarnings || [];
config.ignoreWarnings.push(/Critical dependency: the request of a dependency is an expression/);
// 7. Fix for "webpackEmptyContext" in sqlite3.mjs
config.plugins.push(
new webpack.ContextReplacementPlugin(
/@sqlite\.org\/sqlite-wasm/,
(data) => {
delete data.dependencies;
return data;
}
)
);
// 8. MIME types
config.devServer = config.devServer || {};
config.devServer.devMiddleware = config.devServer.devMiddleware || {};
config.devServer.devMiddleware.mimeTypes = {
'application/wasm': ['wasm'],
'application/javascript': ['js']
};
@@ -1,9 +0,0 @@
// Fix für sql.js unter Webpack 5
config.resolve = config.resolve || {};
config.resolve.fallback = config.resolve.fallback || {};
config.resolve.fallback.fs = false;
config.resolve.fallback.path = false;
config.resolve.fallback.crypto = false;
config.resolve.fallback.os = false;
config.resolve.fallback.stream = false;
config.resolve.fallback.buffer = false;